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