Imported Upstream version 2.4.4p3
[debian/amanda] / recover-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.43.2.13.4.6.2.19 2003/03/04 21:13:58 martinea Exp $
28  *
29  * implements the "extract" command in amrecover
30  */
31
32 #include "amanda.h"
33 #include "version.h"
34 #include "amrecover.h"
35 #include "fileheader.h"
36 #include "dgram.h"
37 #include "stream.h"
38 #ifdef SAMBA_CLIENT
39 #include "findpass.h"
40 #endif
41
42 #if defined(KRB4_SECURITY)
43 #include "krb4-security.h"
44 #endif
45 #include "util.h"
46
47 typedef struct EXTRACT_LIST_ITEM
48 {
49     char path[1024];
50
51     struct EXTRACT_LIST_ITEM *next;
52 }
53 EXTRACT_LIST_ITEM;
54
55 typedef struct EXTRACT_LIST
56 {
57     char date[11];                      /* date tape created */
58     int  level;                         /* level of dump */
59     char tape[256];                     /* tape label */
60     int fileno;                         /* fileno on tape */
61     EXTRACT_LIST_ITEM *files;           /* files to get off tape */
62
63     struct EXTRACT_LIST *next;
64 }
65 EXTRACT_LIST;
66
67 #define SKIP_TAPE 2
68 #define RETRY_TAPE 3
69
70 char *dump_device_name = NULL;
71
72 extern char *localhost;
73
74 /* global pid storage for interrupt handler */
75 pid_t extract_restore_child_pid = -1;
76
77
78 static EXTRACT_LIST *extract_list = NULL;
79
80 #ifdef SAMBA_CLIENT
81 unsigned short samba_extract_method = SAMBA_TAR;
82 #endif /* SAMBA_CLIENT */
83
84 #define READ_TIMEOUT    240*60
85
86 static int okay_to_continue P((int, int,  int));
87
88 ssize_t read_buffer(datafd, buffer, buflen)
89 int datafd;
90 char *buffer;
91 size_t buflen;
92 {
93     int maxfd, nfound = 0;
94     ssize_t size = 0;
95     fd_set readset, selectset;
96     struct timeval timeout;
97     char *dataptr;
98     size_t spaceleft;
99     int eof;
100
101     if(datafd < 0 || datafd >= FD_SETSIZE) {
102         errno = EMFILE;                                 /* out of range */
103         return -1;
104     }
105
106     dataptr = buffer;
107     spaceleft = buflen;
108
109     maxfd = datafd + 1;
110     eof = 0;
111
112     FD_ZERO(&readset);
113     FD_SET(datafd, &readset);
114
115     do {
116
117         timeout.tv_sec = READ_TIMEOUT;
118         timeout.tv_usec = 0;
119         memcpy(&selectset, &readset, sizeof(fd_set));
120
121         nfound = select(maxfd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL, &timeout);
122
123         /* check for errors or timeout */
124
125         if(nfound == 0)  {
126             size=-2;
127             fprintf(stderr,"timeout waiting for amrestore\n");
128             fprintf(stderr,"increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n");
129         }
130         if(nfound == -1) {
131             size=-3;
132             fprintf(stderr,"nfound == -1\n");
133         }
134
135         /* read any data */
136
137         if(FD_ISSET(datafd, &selectset)) {
138             size = read(datafd, dataptr, spaceleft);
139             switch(size) {
140             case -1:
141                 break;
142             case 0:
143                 spaceleft -= size;
144                 dataptr += size;
145                 fprintf(stderr,
146                         "EOF, check amidxtaped.<timestamp>.debug file on %s.\n",
147                         tape_server_name);
148                 break;
149             default:
150                 spaceleft -= size;
151                 dataptr += size;
152                 break;
153             }
154         }
155     } while (spaceleft>0 && size>0);
156
157     if(size<0) {
158         return -1;
159     }
160     return (ssize_t)(buflen-spaceleft);
161 }
162
163 EXTRACT_LIST *first_tape_list P((void))
164 {
165     return extract_list;
166 }
167
168 EXTRACT_LIST *next_tape_list(list)
169 EXTRACT_LIST *list;
170 {
171     if (list == NULL)
172         return NULL;
173     return list->next;
174 }
175
176 static void clear_tape_list(tape_list)
177 EXTRACT_LIST *tape_list;
178 {
179     EXTRACT_LIST_ITEM *this, *next;
180
181     this = tape_list->files;
182     while (this != NULL)
183     {
184         next = this->next;
185         free(this);
186         this = next;
187     }
188     tape_list->files = NULL;
189 }
190
191
192 /* remove a tape list from the extract list, clearing the tape list
193    beforehand if necessary */
194 void delete_tape_list(tape_list)
195 EXTRACT_LIST *tape_list;
196 {
197     EXTRACT_LIST *this, *prev;
198
199     /* is it first on the list? */
200     if (tape_list == extract_list)
201     {
202         clear_tape_list(tape_list);
203         extract_list = tape_list->next;
204         amfree(tape_list);
205         return;
206     }
207
208     /* so not first on list - find it and delete */
209     prev = extract_list;
210     this = extract_list->next;
211     while (this != NULL)
212     {
213         if (this == tape_list)
214         {
215             clear_tape_list(tape_list);
216             prev->next = tape_list->next;
217             amfree(tape_list);
218             return;
219         }
220         prev = this;
221         this = this->next;
222     }
223     /*NOTREACHED*/
224 }
225
226
227 /* return the number of files on a tape's list */
228 int length_of_tape_list(tape_list)
229 EXTRACT_LIST *tape_list;
230 {
231     EXTRACT_LIST_ITEM *fn;
232     int n;
233
234     n = 0;
235     for (fn = tape_list->files; fn != NULL; fn = fn->next)
236         n++;
237
238     return n;
239 }
240
241
242 void clear_extract_list P((void))
243 {
244     while (extract_list != NULL)
245         delete_tape_list(extract_list);
246 }
247
248
249 /* returns -1 if error */
250 /* returns  0 on succes */
251 /* returns  1 if already added */
252 static int add_extract_item(ditem)
253 DIR_ITEM *ditem;
254 {
255     EXTRACT_LIST *this, *this1;
256     EXTRACT_LIST_ITEM *that, *curr;
257     char *ditem_path = NULL;
258
259     ditem_path = stralloc(ditem->path);
260     clean_pathname(ditem_path);
261
262     for (this = extract_list; this != NULL; this = this->next)
263     {
264         /* see if this is the list for the tape */      
265         if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
266         {
267             /* yes, so add to list */
268             curr=this->files;
269             while(curr!=NULL)
270             {
271                 if (strcmp(curr->path,ditem_path) == 0) {
272                     amfree(ditem_path);
273                     return 1;
274                 }
275                 curr=curr->next;
276             }
277             that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
278             strncpy(that->path, ditem_path, sizeof(that->path)-1);
279             that->path[sizeof(that->path)-1] = '\0';
280             that->next = this->files;
281             this->files = that;         /* add at front since easiest */
282             amfree(ditem_path);
283             return 0;
284         }
285     }
286
287     /* so this is the first time we have seen this tape */
288     this = (EXTRACT_LIST *)alloc(sizeof(EXTRACT_LIST));
289     strncpy(this->tape, ditem->tape, sizeof(this->tape)-1);
290     this->tape[sizeof(this->tape)-1] ='\0';
291     this->level = ditem->level;
292     this->fileno = ditem->fileno;
293     strncpy(this->date, ditem->date, sizeof(this->date)-1);
294     this->date[sizeof(this->date)-1] = '\0';
295     that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
296     strncpy(that->path, ditem_path, sizeof(that->path)-1);
297     that->path[sizeof(that->path)-1] = '\0';
298     that->next = NULL;
299     this->files = that;
300
301     /* add this in date increasing order          */
302     /* because restore must be done in this order */
303     /* add at begining */
304     if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0) 
305     {
306         this->next = extract_list;
307         extract_list = this;
308         amfree(ditem_path);
309         return 0;
310     }
311     for (this1 = extract_list; this1->next != NULL; this1 = this1->next)
312     {
313         /* add in the middle */
314         if(strcmp(this->date,this1->next->date) < 0)
315         {
316             this->next = this1->next;
317             this1->next = this;
318             amfree(ditem_path);
319             return 0;
320         }
321     }
322     /* add at end */
323     this->next = NULL;
324     this1->next = this;
325     amfree(ditem_path);
326     return 0;
327 }
328
329
330 /* returns -1 if error */
331 /* returns  0 on deletion */
332 /* returns  1 if not there */
333 static int delete_extract_item(ditem)
334 DIR_ITEM *ditem;
335 {
336     EXTRACT_LIST *this;
337     EXTRACT_LIST_ITEM *that, *prev;
338     char *ditem_path = NULL;
339
340     ditem_path = stralloc(ditem->path);
341     clean_pathname(ditem_path);
342
343     for (this = extract_list; this != NULL; this = this->next)
344     {
345         /* see if this is the list for the tape */      
346         if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
347         {
348             /* yes, so find file on list */
349             that = this->files;
350             if (strcmp(that->path, ditem_path) == 0)
351             {
352                 /* first on list */
353                 this->files = that->next;
354                 amfree(that);
355                 /* if list empty delete it */
356                 if (this->files == NULL)
357                     delete_tape_list(this);
358                 amfree(ditem_path);
359                 return 0;
360             }
361             prev = that;
362             that = that->next;
363             while (that != NULL)
364             {
365                 if (strcmp(that->path, ditem_path) == 0)
366                 {
367                     prev->next = that->next;
368                     amfree(that);
369                     amfree(ditem_path);
370                     return 0;
371                 }
372                 prev = that;
373                 that = that->next;
374             }
375             amfree(ditem_path);
376             return 1;
377         }
378     }
379
380     amfree(ditem_path);
381     return 1;
382 }
383
384
385 void add_glob(glob)
386 char *glob;
387 {
388     char *regex;
389     char *regex_path;
390     char *s;
391
392     regex = glob_to_regex(glob);
393     dbprintf(("add_glob (%s) -> %s\n", glob, regex));
394     if ((s = validate_regexp(regex)) != NULL) {
395         printf("\"%s\" is not a valid shell wildcard pattern: ", glob);
396         puts(s);
397         return;
398     }
399     /*
400      * glob_to_regex() anchors the beginning of the pattern with ^,
401      * but we will be tacking it onto the end of the current directory
402      * in add_file, so strip that off.  Also, it anchors the end with
403      * $, but we need to match an optional trailing /, so tack that on
404      * the end.
405      */
406     regex_path = stralloc(regex + 1);
407     amfree(regex);
408     regex_path[strlen(regex_path) - 1] = '\0';
409     strappend(regex_path, "[/]*$");
410     add_file(glob, regex_path);
411     amfree(regex_path);
412 }
413
414 void add_regex(regex)
415 char *regex;
416 {
417     char *s;
418
419     if ((s = validate_regexp(regex)) != NULL) {
420         printf("\"%s\" is not a valid regular expression: ", regex);
421         puts(s);
422         return;
423     }
424     add_file(regex, regex);
425 }
426
427 void add_file(path, regex)
428 char *path;
429 char *regex;
430 {
431     DIR_ITEM *ditem, lditem;
432     char *path_on_disk = NULL;
433     char *path_on_disk_slash = NULL;
434     char *cmd = NULL;
435     char *err = NULL;
436     int i;
437     int j;
438     char *dir, *dir_undo, dir_undo_ch = '\0';
439     char *ditem_path = NULL;
440     char *l = NULL;
441     int  added;
442     char *s, *fp;
443     int ch;
444     int found_one;
445
446     if (disk_path == NULL) {
447         printf("Must select directory before adding files\n");
448         return;
449     }
450
451     dbprintf(("add_file: Looking for \"%s\"\n", regex));
452
453     /* remove "/" at end of path */
454     j = strlen(regex)-1;
455     while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
456
457     /* convert path (assumed in cwd) to one on disk */
458     if (strcmp(disk_path, "/") == 0)
459         path_on_disk = stralloc2("/", regex);
460     else {
461         char *clean_disk_path = clean_regex(disk_path);
462         path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
463         amfree(clean_disk_path);
464     }
465
466     path_on_disk_slash = stralloc2(path_on_disk, "/");
467
468     dbprintf(("add_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n",
469               regex, path_on_disk));
470
471     found_one = 0;
472     for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
473     {
474         dbprintf(("add_file: Pondering ditem->path=\"%s\"\n", ditem->path));
475         if (match(path_on_disk, ditem->path)
476             || match(path_on_disk_slash, ditem->path))
477         {
478             found_one = 1;
479             j = strlen(ditem->path);
480             if((j > 0 && ditem->path[j-1] == '/')
481                || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
482             {   /* It is a directory */
483
484                 ditem_path = newstralloc(ditem_path, ditem->path);
485                 clean_pathname(ditem_path);
486
487                 cmd = stralloc2("ORLD ", ditem_path);
488                 if(send_command(cmd) == -1) {
489                     amfree(cmd);
490                     amfree(ditem_path);
491                     amfree(path_on_disk);
492                     amfree(path_on_disk_slash);
493                     exit(1);
494                 }
495                 amfree(cmd);
496                 /* skip preamble */
497                 if ((i = get_reply_line()) == -1) {
498                     amfree(ditem_path);
499                     amfree(path_on_disk);
500                     amfree(path_on_disk_slash);
501                     exit(1);
502                 }
503                 if(i==0)                /* assume something wrong */
504                 {
505                     amfree(ditem_path);
506                     amfree(path_on_disk);
507                     amfree(path_on_disk_slash);
508                     l = reply_line();
509                     printf("%s\n", l);
510                     return;
511                 }
512                 amfree(err);
513                 dir_undo = NULL;
514                 added=0;
515                 strncpy(lditem.path, ditem_path, sizeof(lditem.path)-1);
516                 lditem.path[sizeof(lditem.path)-1] = '\0';
517                 /* skip the last line -- duplicate of the preamble */
518                 while ((i = get_reply_line()) != 0)
519                 {
520                     if (i == -1) {
521                         amfree(ditem_path);
522                         amfree(path_on_disk);
523                         amfree(path_on_disk_slash);
524                         exit(1);
525                     }
526                     if(err) {
527                         if(cmd == NULL) {
528                             if(dir_undo) *dir_undo = dir_undo_ch;
529                             dir_undo = NULL;
530                             cmd = stralloc(l);  /* save for error report */
531                         }
532                         continue;       /* throw the rest of the lines away */
533                     }
534                     l=reply_line();
535                     if (!server_happy()) {
536                         puts(l);
537                         continue;
538                     }
539 #define sc "201-"
540                     if(strncmp(l, sc, sizeof(sc)-1) != 0) {
541                         err = "bad reply: not 201-";
542                         continue;
543                     }
544
545                     s = l + sizeof(sc)-1;
546                     ch = *s++;
547 #undef sc
548                     skip_whitespace(s, ch);
549                     if(ch == '\0') {
550                         err = "bad reply: missing date field";
551                         continue;
552                     }
553                     copy_string(s, ch, lditem.date, sizeof(lditem.date), fp);
554                     if(fp == NULL) {
555                         err = "bad reply: date field too large";
556                         continue;
557                     }
558
559                     skip_whitespace(s, ch);
560                     if(ch == '\0' || sscanf(s - 1, "%d", &lditem.level) != 1) {
561                         err = "bad reply: cannot parse level field";
562                         continue;
563                     }
564                     skip_integer(s, ch);
565
566                     skip_whitespace(s, ch);
567                     if(ch == '\0') {
568                         err = "bad reply: missing tape field";
569                         continue;
570                     }
571                     copy_string(s, ch, lditem.tape, sizeof(lditem.tape), fp);
572                     if(fp == NULL) {
573                         err = "bad reply: tape field too large";
574                         continue;
575                     }
576
577                     if(am_has_feature(their_features, fe_amindexd_fileno_in_ORLD)) {
578                         skip_whitespace(s, ch);
579                         if(ch == '\0' || sscanf(s - 1, "%d", &lditem.fileno) != 1) {
580                             err = "bad reply: cannot parse fileno field";
581                             continue;
582                         }
583                         skip_integer(s, ch);
584                     }
585
586                     skip_whitespace(s, ch);
587                     if(ch == '\0') {
588                         err = "bad reply: missing directory field";
589                         continue;
590                     }
591                     dir = s - 1;
592                     skip_non_whitespace(s, ch);
593                     dir_undo = s - 1;
594                     dir_undo_ch = *dir_undo;
595                     *dir_undo = '\0';
596
597                     switch(add_extract_item(&lditem)) {
598                     case -1:
599                         printf("System error\n");
600                         dbprintf(("add_file: (Failed) System error\n"));
601                         break;
602                     case  0:
603                         printf("Added dir %s at date %s\n",
604                                ditem_path, lditem.date);
605                         dbprintf(("add_file: (Successful) Added dir %s at date %s\n",
606                                   ditem_path,lditem.date));
607                         added=1;
608                         break;
609                     case  1:
610                         break;
611                     }
612                 }
613                 if(!server_happy()) {
614                     puts(reply_line());
615                 } else if(err) {
616                     if(*err) {
617                         puts(err);
618                     }
619                     puts(cmd);
620                 } else if(added == 0) {
621                     printf("dir %s already added\n", ditem_path);
622                     dbprintf(("add_file: dir %s already added\n", ditem_path));
623                 }
624             }
625             else /* It is a file */
626             {
627                 switch(add_extract_item(ditem)) {
628                 case -1:
629                     printf("System error\n");
630                     dbprintf(("add_file: (Failed) System error\n"));
631                     break;
632                 case  0:
633                     printf("Added %s\n", ditem->path);
634                     dbprintf(("add_file: (Successful) Added %s\n",
635                               ditem->path));
636                     break;
637                 case  1:
638                     printf("File %s already added\n", ditem->path);
639                     dbprintf(("add_file: file %s already added\n",
640                               ditem->path));
641                     break;
642                 }
643             }
644         }
645     }
646     amfree(cmd);
647     amfree(ditem_path);
648     amfree(path_on_disk);
649     amfree(path_on_disk_slash);
650
651     if(! found_one) {
652         printf("File %s doesn't exist in directory\n", path);
653         dbprintf(("add_file: (Failed) File %s doesn't exist in directory\n",
654                   path));
655     }
656 }
657
658
659 void delete_glob(glob)
660 char *glob;
661 {
662     char *regex;
663     char *regex_path;
664     char *s;
665
666     regex = glob_to_regex(glob);
667     dbprintf(("delete_glob (%s) -> %s\n", glob, regex));
668     if ((s = validate_regexp(regex)) != NULL) {
669         printf("\"%s\" is not a valid shell wildcard pattern: ", glob);
670         puts(s);
671         return;
672     }
673     /*
674      * glob_to_regex() anchors the beginning of the pattern with ^,
675      * but we will be tacking it onto the end of the current directory
676      * in add_file, so strip that off.  Also, it anchors the end with
677      * $, but we need to match an optional trailing /, so tack that on
678      * the end.
679      */
680     regex_path = stralloc(regex + 1);
681     amfree(regex);
682     regex_path[strlen(regex_path) - 1] = '\0';
683     strappend(regex_path, "[/]*$");
684     delete_file(glob, regex_path);
685     amfree(regex_path);
686 }
687
688 void delete_regex(regex)
689 char *regex;
690 {
691     char *s;
692
693     if ((s = validate_regexp(regex)) != NULL) {
694         printf("\"%s\" is not a valid regular expression: ", regex);
695         puts(s);
696         return;
697     }
698     delete_file(regex, regex);
699 }
700
701 void delete_file(path, regex)
702 char *path;
703 char *regex;
704 {
705     DIR_ITEM *ditem, lditem;
706     char *path_on_disk = NULL;
707     char *path_on_disk_slash = NULL;
708     char *cmd = NULL;
709     char *err = NULL;
710     int i;
711     int j;
712     char *date, *date_undo, date_undo_ch = '\0';
713     char *tape, *tape_undo, tape_undo_ch = '\0';
714     char *dir, *dir_undo, dir_undo_ch = '\0';
715     int  level, fileno;
716     char *ditem_path = NULL;
717     char *l = NULL;
718     int  deleted;
719     char *s;
720     int ch;
721     int found_one;
722
723     if (disk_path == NULL) {
724         printf("Must select directory before deleting files\n");
725         return;
726     }
727
728     dbprintf(("delete_file: Looking for \"%s\"\n", path));
729     /* remove "/" at the end of the path */
730     j = strlen(regex)-1;
731     while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
732
733     /* convert path (assumed in cwd) to one on disk */
734     if (strcmp(disk_path, "/") == 0)
735         path_on_disk = stralloc2("/", regex);
736     else {
737         char *clean_disk_path = clean_regex(disk_path);
738         path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
739         amfree(clean_disk_path);
740     }
741
742     path_on_disk_slash = stralloc2(path_on_disk, "/");
743
744     dbprintf(("delete_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n",
745               regex, path_on_disk));
746     found_one = 0;
747     for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
748     {
749         dbprintf(("delete_file: Pondering ditem->path=\"%s\"\n", ditem->path));
750         if (match(path_on_disk, ditem->path)
751             || match(path_on_disk_slash, ditem->path))
752         {
753             found_one = 1;
754             j = strlen(ditem->path);
755             if((j > 0 && ditem->path[j-1] == '/')
756                || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
757             {   /* It is a directory */
758                 ditem_path = newstralloc(ditem_path, ditem->path);
759                 clean_pathname(ditem_path);
760
761                 cmd = stralloc2("ORLD ", ditem_path);
762                 if(send_command(cmd) == -1) {
763                     amfree(cmd);
764                     amfree(ditem_path);
765                     amfree(path_on_disk);
766                     amfree(path_on_disk_slash);
767                     exit(1);
768                 }
769                 amfree(cmd);
770                 /* skip preamble */
771                 if ((i = get_reply_line()) == -1) {
772                     amfree(ditem_path);
773                     amfree(path_on_disk);
774                     amfree(path_on_disk_slash);
775                     exit(1);
776                 }
777                 if(i==0)                /* assume something wrong */
778                 {
779                     amfree(ditem_path);
780                     amfree(path_on_disk);
781                     amfree(path_on_disk_slash);
782                     l = reply_line();
783                     printf("%s\n", l);
784                     return;
785                 }
786                 deleted=0;
787                 strncpy(lditem.path, ditem->path, sizeof(lditem.path)-1);
788                 lditem.path[sizeof(lditem.path)-1] = '\0';
789                 amfree(cmd);
790                 amfree(err);
791                 date_undo = tape_undo = dir_undo = NULL;
792                 /* skip the last line -- duplicate of the preamble */
793                 while ((i = get_reply_line()) != 0)
794                 {
795                     if (i == -1) {
796                         amfree(ditem_path);
797                         amfree(path_on_disk);
798                         amfree(path_on_disk_slash);
799                         exit(1);
800                     }
801                     if(err) {
802                         if(cmd == NULL) {
803                             if(tape_undo) *tape_undo = tape_undo_ch;
804                             if(dir_undo) *dir_undo = dir_undo_ch;
805                             date_undo = tape_undo = dir_undo = NULL;
806                             cmd = stralloc(l);  /* save for the error report */
807                         }
808                         continue;       /* throw the rest of the lines away */
809                     }
810                     l=reply_line();
811                     if (!server_happy()) {
812                         puts(l);
813                         continue;
814                     }
815 #define sc "201-"
816                     if(strncmp(l, sc, sizeof(sc)-1) != 0) {
817                         err = "bad reply: not 201-";
818                         continue;
819                     }
820                     s = l + sizeof(sc)-1;
821                     ch = *s++;
822 #undef sc
823                     skip_whitespace(s, ch);
824                     if(ch == '\0') {
825                         err = "bad reply: missing date field";
826                         continue;
827                     }
828                     date = s - 1;
829                     skip_non_whitespace(s, ch);
830                     date_undo = s - 1;
831                     date_undo_ch = *date_undo;
832                     *date_undo = '\0';
833
834                     skip_whitespace(s, ch);
835                     if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
836                         err = "bad reply: cannot parse level field";
837                         continue;
838                     }
839                     skip_integer(s, ch);
840
841                     skip_whitespace(s, ch);
842                     if(ch == '\0') {
843                         err = "bad reply: missing tape field";
844                         continue;
845                     }
846                     tape = s - 1;
847                     skip_non_whitespace(s, ch);
848                     tape_undo = s - 1;
849                     tape_undo_ch = *tape_undo;
850                     *tape_undo = '\0';
851
852                     if(am_has_feature(their_features, fe_amindexd_fileno_in_ORLD)) {
853                         skip_whitespace(s, ch);
854                         if(ch == '\0' || sscanf(s - 1, "%d", &fileno) != 1) {
855                             err = "bad reply: cannot parse fileno field";
856                             continue;
857                         }
858                         skip_integer(s, ch);
859                     }
860
861                     skip_whitespace(s, ch);
862                     if(ch == '\0') {
863                         err = "bad reply: missing directory field";
864                         continue;
865                     }
866                     dir = s - 1;
867                     skip_non_whitespace(s, ch);
868                     dir_undo = s - 1;
869                     dir_undo_ch = *dir_undo;
870                     *dir_undo = '\0';
871
872                     strncpy(lditem.date, date, sizeof(lditem.date)-1);
873                     lditem.date[sizeof(lditem.date)-1] = '\0';
874                     lditem.level=level;
875                     strncpy(lditem.tape, tape, sizeof(lditem.tape)-1);
876                     lditem.tape[sizeof(lditem.tape)-1] = '\0';
877                     switch(delete_extract_item(&lditem)) {
878                     case -1:
879                         printf("System error\n");
880                         dbprintf(("delete_file: (Failed) System error\n"));
881                         break;
882                     case  0:
883                         printf("Deleted dir %s at date %s\n", ditem_path, date);
884                         dbprintf(("delete_file: (Successful) Deleted dir %s at date %s\n",
885                                   ditem_path, date));
886                         deleted=1;
887                         break;
888                     case  1:
889                         break;
890                     }
891                 }
892                 if(!server_happy()) {
893                     puts(reply_line());
894                 } else if(err) {
895                     if(*err) {
896                         puts(err);
897                     }
898                     puts(cmd);
899                 } else if(deleted == 0) {
900                     printf("Warning - dir '%s' not on tape list\n",
901                            ditem_path);
902                     dbprintf(("delete_file: dir '%s' not on tape list\n",
903                               ditem_path));
904                 }
905             }
906             else
907             {
908                 switch(delete_extract_item(ditem)) {
909                 case -1:
910                     printf("System error\n");
911                     dbprintf(("delete_file: (Failed) System error\n"));
912                     break;
913                 case  0:
914                     printf("Deleted %s\n", ditem->path);
915                     dbprintf(("delete_file: (Successful) Deleted %s\n",
916                               ditem->path));
917                     break;
918                 case  1:
919                     printf("Warning - file '%s' not on tape list\n",
920                            ditem->path);
921                     dbprintf(("delete_file: file '%s' not on tape list\n",
922                               ditem->path));
923                     break;
924                 }
925             }
926         }
927     }
928     amfree(cmd);
929     amfree(ditem_path);
930     amfree(path_on_disk);
931     amfree(path_on_disk_slash);
932
933     if(! found_one) {
934         printf("File %s doesn't exist in directory\n", path);
935         dbprintf(("delete_file: (Failed) File %s doesn't exist in directory\n",
936                   path));
937     }
938 }
939
940
941 /* print extract list into file. If NULL ptr passed print to screen */
942 void display_extract_list(file)
943 char *file;
944 {
945     EXTRACT_LIST *this;
946     EXTRACT_LIST_ITEM *that;
947     FILE *fp;
948     char *pager;
949     char *pager_command;
950
951     if (file == NULL)
952     {
953         if ((pager = getenv("PAGER")) == NULL)
954         {
955             pager = "more";
956         }
957         /*
958          * Set up the pager command so if the pager is terminated, we do
959          * not get a SIGPIPE back.
960          */
961         pager_command = stralloc2(pager, " ; /bin/cat > /dev/null");
962         if ((fp = popen(pager_command, "w")) == NULL)
963         {
964             printf("Warning - can't pipe through %s\n", pager);
965             fp = stdout;
966         }
967         amfree(pager_command);
968     }
969     else
970     {
971         if ((fp = fopen(file, "w")) == NULL)
972         {
973             printf("Can't open file '%s' to print extract list into\n", file);
974             return;
975         }
976     }
977
978     for (this = extract_list; this != NULL; this = this->next)
979     {
980         fprintf(fp, "TAPE %s LEVEL %d DATE %s\n",
981                 this->tape, this->level, this->date);
982         for (that = this->files; that != NULL; that = that->next)
983             fprintf(fp, "\t%s\n", that->path);
984     }
985
986     if (file == NULL) {
987         apclose(fp);
988     } else {
989         printf("Extract list written to file %s\n", file);
990         afclose(fp);
991     }
992 }
993
994
995 /* returns 0 if extract list empty and 1 if it isn't */
996 int is_extract_list_nonempty P((void))
997 {
998     return (extract_list != NULL);
999 }
1000
1001
1002 /* prints continue prompt and waits for response,
1003    returns 0 if don't, non-0 if do */
1004 static int okay_to_continue(allow_tape, allow_skip, allow_retry)
1005     int allow_tape;
1006     int allow_skip;
1007     int allow_retry;
1008 {
1009     int ch;
1010     int ret = -1;
1011     char *line = NULL;
1012     char *s;
1013     char *prompt;
1014     int get_tape;
1015
1016     get_tape = 0;
1017     while (ret < 0) {
1018         if (get_tape) {
1019             prompt = "New tape device [?]: ";
1020         } else if (allow_tape && allow_skip) {
1021             prompt = "Continue [?/Y/n/s/t]? ";
1022         } else if (allow_tape && !allow_skip) {
1023             prompt = "Continue [?/Y/n/t]? ";
1024         } else if (allow_retry) {
1025             prompt = "Continue [?/Y/n/r]? ";
1026         } else {
1027             prompt = "Continue [?/Y/n]? ";
1028         }
1029         fputs(prompt, stdout);
1030         fflush(stdout); fflush(stderr);
1031         amfree(line);
1032         if ((line = agets(stdin)) == NULL) {
1033             putchar('\n');
1034             clearerr(stdin);
1035             if (get_tape) {
1036                 get_tape = 0;
1037                 continue;
1038             }
1039             ret = 0;
1040             break;
1041         }
1042         s = line;
1043         while ((ch = *s++) != '\0' && isspace(ch)) {}
1044         if (ch == '?') {
1045             if (get_tape) {
1046                 printf("Enter a new device ([host:]device) or \"default\"\n");
1047             } else {
1048                 printf("Enter \"y\"es to continue, \"n\"o to stop");
1049                 if(allow_skip) {
1050                     printf(", \"s\"kip this tape");
1051                 }
1052                 if(allow_retry) {
1053                     printf(" or \"r\"etry this tape");
1054                 }
1055                 if (allow_tape) {
1056                     printf(" or \"t\"ape to change tape drives");
1057                 }
1058                 putchar('\n');
1059             }
1060         } else if (get_tape) {
1061             set_tape(s - 1);
1062             get_tape = 0;
1063         } else if (ch == '\0' || ch == 'Y' || ch == 'y') {
1064             ret = 1;
1065         } else if (allow_tape && (ch == 'T' || ch == 't')) {
1066             get_tape = 1;
1067         } else if (ch == 'N' || ch == 'n') {
1068             ret = 0;
1069         } else if (allow_retry && (ch == 'R' || ch == 'r')) {
1070             ret = RETRY_TAPE;
1071         } else if (allow_skip && (ch == 'S' || ch == 's')) {
1072             ret = SKIP_TAPE;
1073         }
1074     }
1075     amfree(line);
1076     return ret;
1077 }
1078
1079 static void send_to_tape_server(tss, cmd)
1080 int tss;
1081 char *cmd;
1082 {
1083     size_t l, n;
1084     ssize_t s;
1085     char *end;
1086
1087     for (l = 0, n = strlen(cmd); l < n; l += s)
1088         if ((s = write(tss, cmd + l, n - l)) < 0)
1089         {
1090             perror("Error writing to tape server");
1091             exit(101);
1092         }
1093     end = "\r\n";
1094     for (l = 0, n = strlen(end); l < n; l += s)
1095         if ((s = write(tss, end + l, n - l)) < 0)
1096         {
1097             perror("Error writing to tape server");
1098             exit(101);
1099         }
1100 }
1101
1102
1103 /* start up connection to tape server and set commands to initiate
1104    transfer of dump image.
1105    Return tape server socket on success, -1 on error. */
1106 static int extract_files_setup(label, fsf)
1107 char *label;
1108 int fsf;
1109 {
1110     struct servent *sp;
1111     int my_port;
1112     int tape_server_socket;
1113     char *disk_regex = NULL;
1114     char *host_regex = NULL;
1115     char *service_name = NULL;
1116     char *line = NULL;
1117     char *clean_datestamp, *ch, *ch1;
1118
1119     service_name = stralloc2("amidxtape", SERVICE_SUFFIX);
1120
1121     /* get tape server details */
1122     if ((sp = getservbyname(service_name, "tcp")) == NULL)
1123     {
1124         printf("%s/tcp unknown protocol - config error?\n", service_name);
1125         amfree(service_name);
1126         return -1;
1127     }
1128     amfree(service_name);
1129     seteuid(0);                                 /* it either works ... */
1130     setegid(0);
1131     tape_server_socket = stream_client_privileged(tape_server_name,
1132                                                   ntohs(sp->s_port),
1133                                                   -1,
1134                                                   STREAM_BUFSIZE,
1135                                                   &my_port);
1136     if (tape_server_socket < 0)
1137     {
1138         printf("cannot connect to %s: %s\n", tape_server_name, strerror(errno));
1139         return -1;
1140     }
1141     if (my_port >= IPPORT_RESERVED) {
1142         aclose(tape_server_socket);
1143         printf("did not get a reserved port: %d\n", my_port);
1144         return -1;
1145     }
1146     setegid(getgid());
1147     seteuid(getuid());                          /* put it back */
1148
1149     /* do the security thing */
1150 #if defined(KRB4_SECURITY)
1151 #if 0 /* not yet implemented */
1152     if(krb4_auth)
1153     {
1154         line = get_krb_security();
1155     }
1156 #endif /* 0 */
1157 #endif
1158     {
1159         line = get_bsd_security();
1160     }
1161     send_to_tape_server(tape_server_socket, line);
1162     memset(line, '\0', strlen(line));
1163     amfree(line);
1164
1165     disk_regex = alloc(strlen(disk_name) * 2 + 3);
1166
1167     ch = disk_name;
1168     ch1 = disk_regex;
1169
1170     /* we want to force amrestore to only match disk_name exactly */
1171     *(ch1++) = '^';
1172
1173     /* We need to escape some characters first... NT compatibilty crap */
1174     for (; *ch != 0; ch++, ch1++) {
1175         switch (*ch) {     /* done this way in case there are more */
1176         case '$':
1177             *(ch1++) = '\\';
1178             /* no break; we do want to fall through... */
1179         default:
1180             *ch1 = *ch;
1181         }
1182     }
1183
1184     /* we want to force amrestore to only match disk_name exactly */
1185     *(ch1++) = '$';
1186
1187     *ch1 = '\0';
1188
1189     host_regex = alloc(strlen(dump_hostname) * 2 + 3);
1190
1191     ch = dump_hostname;
1192     ch1 = host_regex;
1193
1194     /* we want to force amrestore to only match dump_hostname exactly */
1195     *(ch1++) = '^';
1196
1197     /* We need to escape some characters first... NT compatibilty crap */
1198     for (; *ch != 0; ch++, ch1++) {
1199         switch (*ch) {     /* done this way in case there are more */
1200         case '$':
1201             *(ch1++) = '\\';
1202             /* no break; we do want to fall through... */
1203         default:
1204             *ch1 = *ch;
1205         }
1206     }
1207
1208     /* we want to force amrestore to only match dump_hostname exactly */
1209     *(ch1++) = '$';
1210
1211     *ch1 = '\0';
1212
1213     clean_datestamp = stralloc(dump_datestamp);
1214     for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) {
1215         if(*ch1 != '-') {
1216             *ch = *ch1;
1217             ch++;
1218         }
1219     }
1220     *ch = '\0';
1221
1222     if(am_has_feature(their_features, fe_amidxtaped_header) &&
1223        am_has_feature(their_features, fe_amidxtaped_device) &&
1224        am_has_feature(their_features, fe_amidxtaped_host) &&
1225        am_has_feature(their_features, fe_amidxtaped_disk) &&
1226        am_has_feature(their_features, fe_amidxtaped_datestamp)) {
1227
1228         char *tt = NULL;
1229
1230         if(am_has_feature(their_features, fe_amidxtaped_config)) {
1231             tt = newstralloc2(tt, "CONFIG=", config);
1232             send_to_tape_server(tape_server_socket, tt);
1233         }
1234         if(am_has_feature(their_features, fe_amidxtaped_label) &&
1235            label && label[0] != '/') {
1236             tt = newstralloc2(tt,"LABEL=",label);
1237             send_to_tape_server(tape_server_socket, tt);
1238         }
1239         if(am_has_feature(their_features, fe_amidxtaped_fsf)) {
1240             char v_fsf[100];
1241             ap_snprintf(v_fsf, 99, "%d", fsf);
1242             tt = newstralloc2(tt, "FSF=",v_fsf);
1243             send_to_tape_server(tape_server_socket, tt);
1244         }
1245         send_to_tape_server(tape_server_socket, "HEADER");
1246         tt = newstralloc2(tt, "DEVICE=", dump_device_name);
1247         send_to_tape_server(tape_server_socket, tt);
1248         tt = newstralloc2(tt, "HOST=", host_regex);
1249         send_to_tape_server(tape_server_socket, tt);
1250         tt = newstralloc2(tt, "DISK=", disk_regex);
1251         send_to_tape_server(tape_server_socket, tt);
1252         tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp);
1253         send_to_tape_server(tape_server_socket, tt);
1254         send_to_tape_server(tape_server_socket, "END");
1255         amfree(tt);
1256     }
1257     else if(1 /* am_has_feature(their_features, fe_amidxtaped_nargs) */) {
1258         /* 2.4.3 doesn't set fe_amidxtaped_nargs but support it */
1259         /* must be supported without test until 2005 */
1260
1261         /* send to the tape server what tape file we want */
1262         /* 6 args:
1263          *   "-h"
1264          *   "-p"
1265          *   "tape device"
1266          *   "hostname"
1267          *   "diskname"
1268          *   "datestamp"
1269          */
1270         send_to_tape_server(tape_server_socket, "6");
1271         send_to_tape_server(tape_server_socket, "-h");
1272         send_to_tape_server(tape_server_socket, "-p");
1273         send_to_tape_server(tape_server_socket, dump_device_name);
1274         send_to_tape_server(tape_server_socket, host_regex);
1275         send_to_tape_server(tape_server_socket, disk_regex);
1276         send_to_tape_server(tape_server_socket, clean_datestamp);
1277
1278         dbprintf(("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n",
1279                   dump_device_name, host_regex, disk_regex, clean_datestamp));
1280     }
1281
1282     amfree(disk_regex);
1283     amfree(host_regex);
1284     amfree(clean_datestamp);
1285
1286     return tape_server_socket;
1287 }
1288
1289
1290 size_t read_file_header(buffer, file, buflen, tapedev)
1291 char *buffer;
1292 dumpfile_t *file;
1293 size_t buflen;
1294 int tapedev;
1295 /*
1296  * Reads the first block of a tape file.
1297  */
1298 {
1299     ssize_t bytes_read;
1300     bytes_read=read_buffer(tapedev,buffer,buflen);
1301     if(bytes_read < 0) {
1302         error("error reading tape: %s", strerror(errno));
1303     }
1304     else if((size_t)bytes_read < buflen) {
1305         fprintf(stderr, "%s: short block %d byte%s\n",
1306                 get_pname(), (int)bytes_read, (bytes_read == 1) ? "" : "s");
1307         print_header(stdout, file);
1308         error("Can't read file header");
1309     }
1310     else { /* bytes_read == buflen */
1311         parse_file_header(buffer, file, bytes_read);
1312     }
1313     return((size_t)bytes_read);
1314 }
1315
1316 enum dumptypes {IS_UNKNOWN, IS_DUMP, IS_GNUTAR, IS_TAR, IS_SAMBA, IS_SAMBA_TAR};
1317
1318 /* exec restore to do the actual restoration */
1319 static void extract_files_child(in_fd, elist)
1320     int in_fd;
1321     EXTRACT_LIST *elist;
1322 {
1323     int save_errno;
1324     int extra_params = 0;
1325     int i,j=0;
1326     char **restore_args = NULL;
1327     int files_off_tape;
1328     EXTRACT_LIST_ITEM *fn;
1329     enum dumptypes dumptype = IS_UNKNOWN;
1330     char buffer[DISK_BLOCK_BYTES];
1331     dumpfile_t file;
1332     size_t buflen;
1333     size_t len_program;
1334     char *cmd = NULL;
1335     int passwd_field = -1;
1336 #ifdef SAMBA_CLIENT
1337     char *domain = NULL, *smbpass = NULL;
1338 #endif
1339
1340     /* code executed by child to do extraction */
1341     /* never returns */
1342
1343     /* make in_fd be our stdin */
1344     if (dup2(in_fd, STDIN_FILENO) == -1)
1345     {
1346         perror("extract_list - extract files client");
1347         exit(1);
1348     }
1349
1350     /* read the file header */
1351     fh_init(&file);
1352     buflen=read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO);
1353
1354     if(buflen == 0 || file.type != F_DUMPFILE) {
1355         print_header(stdout, &file);
1356         error("bad header");
1357     }
1358
1359     if (file.program != NULL) {
1360 #ifdef GNUTAR
1361         if (strcmp(file.program, GNUTAR) == 0)
1362             dumptype = IS_GNUTAR;
1363 #endif
1364
1365         if (dumptype == IS_UNKNOWN) {
1366             len_program = strlen(file.program);
1367             if(len_program >= 3 &&
1368                strcmp(&file.program[len_program-3],"tar") == 0)
1369                 dumptype = IS_TAR;
1370         }
1371
1372 #ifdef SAMBA_CLIENT
1373         if (dumptype == IS_UNKNOWN && strcmp(file.program, SAMBA_CLIENT) ==0) {
1374             if (samba_extract_method == SAMBA_TAR)
1375               dumptype = IS_SAMBA_TAR;
1376             else
1377               dumptype = IS_SAMBA;
1378         }
1379 #endif
1380     }
1381
1382     /* form the arguments to restore */
1383     files_off_tape = length_of_tape_list(elist);
1384     switch (dumptype) {
1385     case IS_SAMBA:
1386 #ifdef SAMBA_CLIENT
1387         extra_params = 10;
1388         break;
1389 #endif
1390     case IS_TAR:
1391     case IS_GNUTAR:
1392     case IS_SAMBA_TAR:
1393         extra_params = 3;
1394         break;
1395     case IS_UNKNOWN:
1396     case IS_DUMP:
1397 #ifdef AIX_BACKUP
1398         extra_params = 2;
1399 #else
1400 #if defined(XFSDUMP)
1401         if (strcmp(file.program, XFSDUMP) == 0) {
1402             extra_params = 4 + files_off_tape;
1403         } else
1404 #endif
1405         {
1406         extra_params = 4;
1407         }
1408 #endif
1409         break;
1410     }
1411
1412     restore_args = (char **)alloc((extra_params + files_off_tape + 1)
1413                                   * sizeof(char *));
1414     switch(dumptype) {
1415     case IS_SAMBA:
1416 #ifdef SAMBA_CLIENT
1417         restore_args[j++] = stralloc("smbclient");
1418         smbpass = findpass(file.disk, &domain);
1419         if (smbpass) {
1420             restore_args[j++] = stralloc(file.disk);
1421             passwd_field=j;
1422             restore_args[j++] = stralloc("-U");
1423             restore_args[j++] = smbpass;
1424             if (domain) {
1425                 restore_args[j++] = stralloc("-W");
1426                 restore_args[j++] = stralloc(domain);
1427             } else
1428                 extra_params -= 2;
1429         } else
1430             extra_params -= 6;
1431         restore_args[j++] = stralloc("-d0");
1432         restore_args[j++] = stralloc("-Tx");
1433         restore_args[j++] = stralloc("-");      /* data on stdin */
1434         break;
1435 #endif
1436     case IS_TAR:
1437     case IS_GNUTAR:
1438         restore_args[j++] = stralloc("tar");
1439         restore_args[j++] = stralloc("-xpGvf");
1440         restore_args[j++] = stralloc("-");      /* data on stdin */
1441         break;
1442     case IS_SAMBA_TAR:
1443         restore_args[j++] = stralloc("tar");
1444         restore_args[j++] = stralloc("-xpvf");
1445         restore_args[j++] = stralloc("-");      /* data on stdin */
1446         break;
1447     case IS_UNKNOWN:
1448     case IS_DUMP:
1449         restore_args[j++] = stralloc("restore");
1450 #ifdef AIX_BACKUP
1451         restore_args[j++] = stralloc("-xB");
1452 #else
1453 #if defined(XFSDUMP)
1454         if (strcmp(file.program, XFSDUMP) == 0) {
1455             restore_args[j++] = stralloc("-v");
1456             restore_args[j++] = stralloc("silent");
1457         } else
1458 #endif
1459 #if defined(VDUMP)
1460         if (strcmp(file.program, VDUMP) == 0) {
1461             restore_args[j++] = stralloc("xf");
1462             restore_args[j++] = stralloc("-");  /* data on stdin */
1463         } else
1464 #endif
1465         {
1466         restore_args[j++] = stralloc("xbf");
1467         restore_args[j++] = stralloc("2");      /* read in units of 1K */
1468         restore_args[j++] = stralloc("-");      /* data on stdin */
1469         }
1470 #endif
1471     }
1472   
1473     for (i = 0, fn = elist->files; i < files_off_tape; i++, fn = fn->next)
1474     {
1475         switch (dumptype) {
1476         case IS_TAR:
1477         case IS_GNUTAR:
1478         case IS_SAMBA_TAR:
1479         case IS_SAMBA:
1480             restore_args[j++] = stralloc2(".", fn->path);
1481             break;
1482         case IS_UNKNOWN:
1483         case IS_DUMP:
1484 #if defined(XFSDUMP)
1485             if (strcmp(file.program, XFSDUMP) == 0) {
1486                 /*
1487                  * xfsrestore needs a -s option before each file to be
1488                  * restored, and also wants them to be relative paths.
1489                  */
1490                 restore_args[j++] = stralloc("-s");
1491                 restore_args[j++] = stralloc(fn->path + 1);
1492             } else
1493 #endif
1494             {
1495             restore_args[j++] = stralloc(fn->path);
1496             }
1497         }
1498     }
1499 #if defined(XFSDUMP)
1500     if (strcmp(file.program, XFSDUMP) == 0) {
1501         restore_args[j++] = stralloc("-");
1502         restore_args[j++] = stralloc(".");
1503     }
1504 #endif
1505     restore_args[j] = NULL;
1506
1507     switch (dumptype) {
1508     case IS_SAMBA:
1509 #ifdef SAMBA_CLIENT
1510         cmd = stralloc(SAMBA_CLIENT);
1511         break;
1512 #else
1513         /* fall through to ... */
1514 #endif
1515     case IS_TAR:
1516     case IS_GNUTAR:
1517     case IS_SAMBA_TAR:
1518 #ifndef GNUTAR
1519         fprintf(stderr, "warning: GNUTAR program not available.\n");
1520         cmd = stralloc("tar");
1521 #else
1522         cmd = stralloc(GNUTAR);
1523 #endif
1524         break;
1525     case IS_UNKNOWN:
1526     case IS_DUMP:
1527         cmd = NULL;
1528 #if defined(DUMP)
1529         if (strcmp(file.program, DUMP) == 0) {
1530             cmd = stralloc(RESTORE);
1531         }
1532 #endif
1533 #if defined(VDUMP)
1534         if (strcmp(file.program, VDUMP) == 0) {
1535             cmd = stralloc(VRESTORE);
1536         }
1537 #endif
1538 #if defined(VXDUMP)
1539         if (strcmp(file.program, VXDUMP) == 0) {
1540             cmd = stralloc(VXRESTORE);
1541         }
1542 #endif
1543 #if defined(XFSDUMP)
1544         if (strcmp(file.program, XFSDUMP) == 0) {
1545             cmd = stralloc(XFSRESTORE);
1546         }
1547 #endif
1548         if (cmd == NULL) {
1549             fprintf(stderr, "warning: restore program for %s not available.\n",
1550                     file.program);
1551             cmd = stralloc("restore");
1552         }
1553     }
1554     if (cmd) {
1555         dbprintf(("Exec'ing %s with arguments:\n", cmd));
1556         for (i = 0; i < j; i++) {
1557             if( i == passwd_field)
1558                 dbprintf(("\tXXXXX\n"));
1559             else
1560                 dbprintf(("\t%s\n", restore_args[i]));
1561         }
1562         (void)execv(cmd, restore_args);
1563         /* only get here if exec failed */
1564         save_errno = errno;
1565         for (i = 0; i < j; i++) {
1566             amfree(restore_args[i]);
1567         }
1568         amfree(restore_args);
1569         errno = save_errno;
1570         perror("amrecover couldn't exec");
1571         fprintf(stderr, " problem executing %s\n", cmd);
1572         amfree(cmd);
1573     }
1574     exit(1);
1575     /*NOT REACHED */
1576 }
1577
1578
1579 /* does the actual extraction of files */
1580 /* The original design had the dump image being returned exactly as it
1581    appears on the tape, and this routine getting from the index server
1582    whether or not it is compressed, on the assumption that the tape
1583    server may not know how to uncompress it. But
1584    - Amrestore can't do that. It returns either compressed or uncompressed
1585    (always). Amrestore assumes it can uncompress files. It is thus a good
1586    idea to run the tape server on a machine with gzip.
1587    - The information about compression in the disklist is really only
1588    for future dumps. It is possible to change compression on a drive
1589    so the information in the disklist may not necessarily relate to
1590    the dump image on the tape.
1591      Consequently the design was changed to assuming that amrestore can
1592    uncompress any dump image and have it return an uncompressed file
1593    always. */
1594 void extract_files P((void))
1595 {
1596     EXTRACT_LIST *elist;
1597     pid_t pid;
1598     amwait_t child_stat;
1599     char buf[STR_SIZE];
1600     char *l;
1601     int tape_server_socket;
1602     int first;
1603     int otc;
1604
1605     if (!is_extract_list_nonempty())
1606     {
1607         printf("Extract list empty - No files to extract!\n");
1608         return;
1609     }
1610
1611     /* get tape device name from index server if none specified */
1612     if (tape_server_name == NULL) {
1613         tape_server_name = newstralloc(tape_server_name, server_name);
1614     }
1615     if (tape_device_name == NULL) {
1616         if (send_command("TAPE") == -1)
1617             exit(1);
1618         if (get_reply_line() == -1)
1619             exit(1);
1620         l = reply_line();
1621         if (!server_happy())
1622         {
1623             printf("%s\n", l);
1624             exit(1);
1625         }
1626         /* skip reply number */
1627         tape_device_name = newstralloc(tape_device_name, l+4);
1628     }
1629
1630     if (strcmp(tape_device_name, "/dev/null") == 0)
1631     {
1632         printf("amrecover: warning: using %s as the tape device will not work\n",
1633                tape_device_name);
1634     }
1635
1636     first=1;
1637     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist))
1638         if(elist->tape[0]!='/') {
1639             if(first) {
1640                 printf("\nExtracting files using tape drive %s on host %s.\n",
1641                         tape_device_name, tape_server_name);
1642                 printf("The following tapes are needed:");
1643                 first=0;
1644             }
1645             else
1646                 printf("                               ");
1647             printf(" %s\n", elist->tape);
1648         }
1649     first=1;
1650     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist))
1651         if(elist->tape[0]=='/') {
1652             if(first) {
1653                 printf("\nExtracting files from holding disk on host %s.\n",
1654                         tape_server_name);
1655                 printf("The following files are needed:");
1656                 first=0;
1657             }
1658             else
1659                 printf("                               ");
1660             printf(" %s\n", elist->tape);
1661         }
1662     printf("\n");
1663     getcwd(buf, sizeof(buf));
1664     printf("Restoring files into directory %s\n", buf);
1665 #ifdef SAMBA_CLIENT
1666     if (samba_extract_method == SAMBA_SMBCLIENT)
1667       printf("(unless it is a Samba backup, that will go through to the SMB server)\n");
1668 #endif
1669     if (!okay_to_continue(0,0,0))
1670         return;
1671     printf("\n");
1672
1673     while ((elist = first_tape_list()) != NULL)
1674     {
1675         if(elist->tape[0]=='/') {
1676             dump_device_name = newstralloc(dump_device_name, elist->tape);
1677             printf("Extracting from file %s\n",dump_device_name);
1678         }
1679         else {
1680             printf("Extracting files using tape drive %s on host %s.\n",
1681                    tape_device_name, tape_server_name);
1682             printf("Load tape %s now\n", elist->tape);
1683             otc = okay_to_continue(1,1,0);
1684             if (otc == 0)
1685                 return;
1686             else if (otc == SKIP_TAPE) {
1687                 delete_tape_list(elist); /* skip this tape */
1688                 continue;
1689             }
1690             dump_device_name = newstralloc(dump_device_name, tape_device_name);
1691         }
1692         dump_datestamp = newstralloc(dump_datestamp, elist->date);
1693
1694         /* connect to the tape handler daemon on the tape drive server */
1695         if ((tape_server_socket = extract_files_setup(elist->tape, elist->fileno)) == -1)
1696         {
1697             fprintf(stderr, "amrecover - can't talk to tape server\n");
1698             return;
1699         }
1700
1701         /* okay, ready to extract. fork a child to do the actual work */
1702         if ((pid = fork()) == 0)
1703         {
1704             /* this is the child process */
1705             /* never gets out of this clause */
1706             extract_files_child(tape_server_socket, elist);
1707             /*NOT REACHED*/
1708         }
1709         /* this is the parent */
1710         if (pid == -1)
1711         {
1712             perror("extract_list - error forking child");
1713             exit(1);
1714         }
1715
1716         /* store the child pid globally so that it can be killed on intr */
1717         extract_restore_child_pid = pid;
1718
1719         aclose(tape_server_socket);
1720
1721         /* wait for the child process to finish */
1722         if ((pid = waitpid(-1, &child_stat, 0)) == (pid_t)-1)
1723         {
1724             perror("extract_list - error waiting for child");
1725             exit(1);
1726         }
1727         if (pid == extract_restore_child_pid)
1728         {
1729             extract_restore_child_pid = -1;
1730         }
1731         else
1732         {
1733             fprintf(stderr, "extract list - unknown child terminated?\n");
1734             exit(1);
1735         }
1736         if ((WIFEXITED(child_stat) != 0) && (WEXITSTATUS(child_stat) != 0))
1737         {
1738             fprintf(stderr,
1739                     "extract_list - child returned non-zero status: %d\n",
1740                     WEXITSTATUS(child_stat));
1741             otc = okay_to_continue(0,0,1);
1742             if(otc == 0)
1743                 return;
1744             else if(otc == 1) {
1745                 delete_tape_list(elist); /* tape failed so delete from list */
1746             }
1747             else { /* RETRY_TAPE */
1748             }
1749         }
1750         else {
1751             delete_tape_list(elist);    /* tape done so delete from list */
1752         }
1753     }
1754 }