Imported Upstream version 2.5.1
[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.117 2006/08/24 01:57:15 paddy_s Exp $
28  *
29  * implements the "extract" command in amrecover
30  */
31
32 #include "amanda.h"
33 #include "version.h"
34 #include "amrecover.h"
35 #include "fileheader.h"
36 #include "dgram.h"
37 #include "stream.h"
38 #include "tapelist.h"
39 #ifdef SAMBA_CLIENT
40 #include "findpass.h"
41 #endif
42 #include "util.h"
43 #include "clientconf.h"
44 #include "protocol.h"
45 #include "event.h"
46 #include "security.h"
47
48 typedef struct EXTRACT_LIST_ITEM {
49     char *path;
50     struct EXTRACT_LIST_ITEM *next;
51 }
52 EXTRACT_LIST_ITEM;
53
54 typedef struct EXTRACT_LIST {
55     char *date;                 /* date tape created */
56     int  level;                 /* level of dump */
57     char *tape;                 /* tape label */
58     off_t fileno;               /* fileno on tape */
59     EXTRACT_LIST_ITEM *files;   /* files to get off tape */
60
61     struct EXTRACT_LIST *next;
62 }
63 EXTRACT_LIST;
64
65 #define SKIP_TAPE 2
66 #define RETRY_TAPE 3
67
68 static struct {
69     const char *name;
70     security_stream_t *fd;
71 } amidxtaped_streams[] = {
72 #define CTLFD  0
73     { "CTL", NULL },
74 #define DATAFD  1
75     { "DATA", NULL },
76 };
77 #define NSTREAMS  (int)(sizeof(amidxtaped_streams) / sizeof(amidxtaped_streams[0]))
78
79
80 static void amidxtaped_response(void *, pkt_t *, security_handle_t *);
81 static void stop_amidxtaped(void);
82 char *amidxtaped_client_get_security_conf(char *, void *);
83 static char *dump_device_name = NULL;
84 static char *errstr;
85 static char *amidxtaped_line = NULL;
86
87 extern char *localhost;
88
89 /* global pid storage for interrupt handler */
90 pid_t extract_restore_child_pid = -1;
91
92 static EXTRACT_LIST *extract_list = NULL;
93 static const security_driver_t *amidxtaped_secdrv;
94
95 #ifdef SAMBA_CLIENT
96 unsigned short samba_extract_method = SAMBA_TAR;
97 #endif /* SAMBA_CLIENT */
98
99 #define READ_TIMEOUT    240*60
100
101 EXTRACT_LIST *first_tape_list(void);
102 EXTRACT_LIST *next_tape_list(EXTRACT_LIST *list);
103 static int is_empty_dir(char *fname);
104 int is_extract_list_nonempty(void);
105 int length_of_tape_list(EXTRACT_LIST *tape_list);
106 void add_file(char *path, char *regex);
107 void add_glob(char *glob);
108 void add_regex(char *regex);
109 void clear_extract_list(void);
110 void clean_tape_list(EXTRACT_LIST *tape_list);
111 void clean_extract_list(void);
112 void check_file_overwrite(char *filename);
113 void delete_file(char *path, char *regex);
114 void delete_glob(char *glob);
115 void delete_regex(char *regex);
116 void delete_tape_list(EXTRACT_LIST *tape_list);
117 void display_extract_list(char *file);
118 void extract_files(void);
119 void read_file_header(char *buffer,
120                         dumpfile_t *file,
121                         size_t buflen,
122                         int tapedev);
123 static int add_extract_item(DIR_ITEM *ditem);
124 static int delete_extract_item(DIR_ITEM *ditem);
125 static int extract_files_setup(char *label, off_t fsf);
126 static int okay_to_continue(int allow_tape,
127                         int allow_skip,
128                         int allow_retry);
129 static ssize_t read_buffer(int datafd,
130                         char *buffer,
131                         size_t buflen,
132                         long timeout_s);
133 static void clear_tape_list(EXTRACT_LIST *tape_list);
134 static void extract_files_child(int in_fd, EXTRACT_LIST *elist);
135 static void send_to_tape_server(security_stream_t *stream, char *cmd);
136 int writer_intermediary(EXTRACT_LIST *elist);
137 int get_amidxtaped_line(void);
138 static void read_amidxtaped_data(void *, void *, ssize_t);
139
140 /*
141  * Function:  ssize_t read_buffer(datafd, buffer, buflen, timeout_s)
142  *
143  * Description:
144  *      read data from input file desciptor waiting up to timeout_s
145  *      seconds before returning data.
146  *
147  * Inputs:
148  *      datafd    - File descriptor to read from.
149  *      buffer    - Buffer to read into.
150  *      buflen    - Maximum number of bytes to read into buffer.
151  *      timeout_s - Seconds to wait before returning what was already read.
152  *
153  * Returns:
154  *      >0        - Number of data bytes in buffer.
155  *       0        - EOF
156  *      -1        - errno == ETIMEDOUT if no data available in specified time.
157  *                  errno == ENFILE if datafd is invalid.
158  *                  otherwise errno is set by select or read..
159  */
160
161 static ssize_t
162 read_buffer(
163     int         datafd,
164     char *      buffer,
165     size_t      buflen,
166     long        timeout_s)
167 {
168     ssize_t size = 0;
169     fd_set readset;
170     struct timeval timeout;
171     char *dataptr;
172     ssize_t spaceleft;
173     int nfound;
174
175     if(datafd < 0 || datafd >= (int)FD_SETSIZE) {
176         errno = EMFILE;                                 /* out of range */
177         return -1;
178     }
179
180     dataptr = buffer;
181     spaceleft = (ssize_t)buflen;
182
183     do {
184         FD_ZERO(&readset);
185         FD_SET(datafd, &readset);
186         timeout.tv_sec = timeout_s;
187         timeout.tv_usec = 0;
188         nfound = select(datafd+1, &readset, NULL, NULL, &timeout);
189         if(nfound < 0 ) {
190             /* Select returned an error. */
191             fprintf(stderr,"select error: %s\n", strerror(errno));
192             size = -1;
193             break;
194         }
195
196         if (nfound == 0) {
197             /* Select timed out. */
198             if (timeout_s != 0)  {
199                 /* Not polling: a real read timeout */
200                 fprintf(stderr,"timeout waiting for restore\n");
201                 fprintf(stderr,"increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n");
202             }
203             errno = ETIMEDOUT;
204             size = -1;
205             break;
206         }
207
208         if(!FD_ISSET(datafd, &readset))
209             continue;
210
211         /* Select says data is available, so read it.  */
212         size = read(datafd, dataptr, (size_t)spaceleft);
213         if (size < 0) {
214             if ((errno == EINTR) || (errno == EAGAIN)) {
215                 continue;
216             }
217             if (errno != EPIPE) {
218                 fprintf(stderr, "read_buffer: read error - %s",
219                     strerror(errno));
220                 break;
221             }
222             size = 0;
223         }
224         spaceleft -= size;
225         dataptr += size;
226     } while ((size > 0) && (spaceleft > 0));
227
228     return ((((ssize_t)buflen-spaceleft) > 0) ? ((ssize_t)buflen-spaceleft) : size);
229 }
230
231
232 EXTRACT_LIST *
233 first_tape_list(void)
234 {
235     return extract_list;
236 }
237
238 EXTRACT_LIST *
239 next_tape_list(
240     /*@keep@*/EXTRACT_LIST *list)
241 {
242     if (list == NULL)
243         return NULL;
244     return list->next;
245 }
246
247 static void
248 clear_tape_list(
249     EXTRACT_LIST *      tape_list)
250 {
251     EXTRACT_LIST_ITEM *this, *next;
252     
253
254     this = tape_list->files;
255     while (this != NULL)
256     {
257         next = this->next;
258         amfree(this->path);
259         amfree(this);
260         this = next;
261     }
262     tape_list->files = NULL;
263 }
264
265
266 /* remove a tape list from the extract list, clearing the tape list
267    beforehand if necessary */
268 void
269 delete_tape_list(
270     EXTRACT_LIST *tape_list)
271 {
272     EXTRACT_LIST *this, *prev;
273
274     if (tape_list == NULL)
275         return;
276
277     /* is it first on the list? */
278     if (tape_list == extract_list)
279     {
280         extract_list = tape_list->next;
281         clear_tape_list(tape_list);
282         amfree(tape_list->date);
283         amfree(tape_list->tape);
284         amfree(tape_list);
285         return;
286     }
287
288     /* so not first on list - find it and delete */
289     prev = extract_list;
290     this = extract_list->next;
291     while (this != NULL)
292     {
293         if (this == tape_list)
294         {
295             prev->next = tape_list->next;
296             clear_tape_list(tape_list);
297             amfree(tape_list->date);
298             amfree(tape_list->tape);
299             amfree(tape_list);
300             return;
301         }
302         prev = this;
303         this = this->next;
304     }
305     /*NOTREACHED*/
306 }
307
308
309 /* return the number of files on a tape's list */
310 int
311 length_of_tape_list(
312     EXTRACT_LIST *tape_list)
313 {
314     EXTRACT_LIST_ITEM *fn;
315     int n;
316
317     n = 0;
318     for (fn = tape_list->files; fn != NULL; fn = fn->next)
319         n++;
320
321     return n;
322 }
323
324
325 void
326 clear_extract_list(void)
327 {
328     while (extract_list != NULL)
329         delete_tape_list(extract_list);
330 }
331
332
333 void
334 clean_tape_list(
335     EXTRACT_LIST *tape_list)
336 {
337     EXTRACT_LIST_ITEM *fn1, *pfn1, *ofn1;
338     EXTRACT_LIST_ITEM *fn2, *pfn2, *ofn2;
339     int remove_fn1;
340     int remove_fn2;
341
342     pfn1 = NULL;
343     fn1 = tape_list->files;
344     while (fn1 != NULL) {
345         remove_fn1 = 0;
346
347         pfn2 = fn1;
348         fn2 = fn1->next;
349         while (fn2 != NULL && remove_fn1 == 0) {
350             remove_fn2 = 0;
351             if(strcmp(fn1->path, fn2->path) == 0) {
352                 remove_fn2 = 1;
353             } else if (strncmp(fn1->path, fn2->path, strlen(fn1->path)) == 0 &&
354                        ((strlen(fn2->path) > strlen(fn1->path) &&
355                          fn2->path[strlen(fn1->path)] == '/') ||
356                        (fn1->path[strlen(fn1->path)-1] == '/'))) {
357                 remove_fn2 = 1;
358             } else if (strncmp(fn2->path, fn1->path, strlen(fn2->path)) == 0 &&
359                        ((strlen(fn1->path) > strlen(fn2->path) &&
360                          fn1->path[strlen(fn2->path)] == '/')  ||
361                        (fn2->path[strlen(fn2->path)-1] == '/'))) {
362                 remove_fn1 = 1;
363                 break;
364             }
365
366             if (remove_fn2) {
367                 dbprintf(("removing path %s, it is included in %s\n",
368                           fn2->path, fn1->path));
369                 ofn2 = fn2;
370                 fn2 = fn2->next;
371                 amfree(ofn2->path);
372                 amfree(ofn2);
373                 pfn2->next = fn2;
374             } else if (remove_fn1 == 0) {
375                 pfn2 = fn2;
376                 fn2 = fn2->next;
377             }
378         }
379
380         if(remove_fn1 != 0) {
381             /* fn2->path is always valid */
382             /*@i@*/ dbprintf(("removing path %s, it is included in %s\n",
383             /*@i@*/           fn1->path, fn2->path));
384             ofn1 = fn1;
385             fn1 = fn1->next;
386             amfree(ofn1->path);
387             if(pfn1 == NULL) {
388                 amfree(tape_list->files);
389                 tape_list->files = fn1;
390             } else {
391                 amfree(pfn1->next);
392                 pfn1->next = fn1;
393             }
394         } else {
395             pfn1 = fn1;
396             fn1 = fn1->next;
397         }
398     }
399 }
400
401
402 void
403 clean_extract_list(void)
404 {
405     EXTRACT_LIST *this;
406
407     for (this = extract_list; this != NULL; this = this->next)
408         clean_tape_list(this);
409 }
410
411
412 int add_to_unlink_list(char *path);
413 int do_unlink_list(void);
414 void free_unlink_list(void);
415
416 typedef struct s_unlink_list {
417     char *path;
418     struct s_unlink_list *next;
419 } t_unlink_list;
420 t_unlink_list *unlink_list = NULL;
421
422 int
423 add_to_unlink_list(
424     char *path)
425 {
426     t_unlink_list *ul;
427
428     if (!unlink_list) {
429         unlink_list = alloc(SIZEOF(*unlink_list));
430         unlink_list->path = stralloc(path);
431         unlink_list->next = NULL;
432     } else {
433         for (ul = unlink_list; ul != NULL; ul = ul->next) {
434             if (strcmp(ul->path, path) == 0)
435                 return 0;
436         }
437         ul = alloc(SIZEOF(*ul));
438         ul->path = stralloc(path);
439         ul->next = unlink_list;
440         unlink_list = ul;
441     }
442     return 1;
443 }
444
445 int
446 do_unlink_list(void)
447 {
448     t_unlink_list *ul;
449     int ret = 1;
450
451     for (ul = unlink_list; ul != NULL; ul = ul->next) {
452         if (unlink(ul->path) < 0) {
453             fprintf(stderr,"Can't unlink %s: %s\n", ul->path, strerror(errno));
454             ret = 0;
455         }
456     }
457     return ret;
458 }
459
460
461 void
462 free_unlink_list(void)
463 {
464     t_unlink_list *ul, *ul1;
465
466     for (ul = unlink_list; ul != NULL; ul = ul1) {
467         amfree(ul->path);
468         ul1 = ul->next;
469         amfree(ul);
470     }
471
472     unlink_list = NULL;
473 }
474
475
476
477 void
478 check_file_overwrite(
479     char *dir)
480 {
481     EXTRACT_LIST      *this;
482     EXTRACT_LIST_ITEM *fn;
483     struct stat        stat_buf;
484     char              *filename;
485     char              *path, *s;
486
487     for (this = extract_list; this != NULL; this = this->next) {
488         for (fn = this->files; fn != NULL ; fn = fn->next) {
489
490             /* Check path component of fn->path */
491
492             path = stralloc2(dir, fn->path);
493             if (path[strlen(path)-1] == '/') {
494                 path[strlen(path)-1] = '\0';
495             }
496
497             s = path + strlen(dir) + 1;
498             while((s = strchr(s, '/'))) {
499                 *s = '\0';
500                 if (lstat(path, &stat_buf) == 0) {
501                     if(!S_ISDIR(stat_buf.st_mode)) {
502                         if (add_to_unlink_list(path)) {
503                             printf("WARNING: %s is not a directory, "
504                                    "it will be deleted.\n",
505                                    path);
506                         }
507                     }
508                 }
509                 else if (errno != ENOENT) {
510                     printf("Can't stat %s: %s\n", path, strerror(errno));
511                 }
512                 *s = '/';
513                 s++;
514             }
515             amfree(path);
516
517             /* Check fn->path */
518
519             filename = stralloc2(dir, fn->path);
520             if (filename[strlen(filename)-1] == '/') {
521                 filename[strlen(filename)-1] = '\0';
522             }
523
524             if (lstat(filename, &stat_buf) == 0) {
525                 if(S_ISDIR(stat_buf.st_mode)) {
526                     if(!is_empty_dir(filename)) {
527                         printf("WARNING: All existing files in %s "
528                                "will be deleted.\n", filename);
529                     }
530                 } else if(S_ISREG(stat_buf.st_mode)) {
531                     printf("WARNING: Existing file %s will be overwritten\n",
532                            filename);
533                 } else {
534                     if (add_to_unlink_list(filename)) {
535                         printf("WARNING: Existing entry %s will be deleted\n",
536                                filename);
537                     }
538                 }
539             } else if (errno != ENOENT) {
540                 printf("Can't stat %s: %s\n", filename, strerror(errno));
541             }
542             amfree(filename);
543         }
544     }
545 }
546
547
548 /* returns -1 if error */
549 /* returns  0 on succes */
550 /* returns  1 if already added */
551 static int
552 add_extract_item(
553     DIR_ITEM *ditem)
554 {
555     EXTRACT_LIST *this, *this1;
556     EXTRACT_LIST_ITEM *that, *curr;
557     char *ditem_path = NULL;
558
559     ditem_path = stralloc(ditem->path);
560     clean_pathname(ditem_path);
561
562     for (this = extract_list; this != NULL; this = this->next)
563     {
564         /* see if this is the list for the tape */      
565         if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
566         {
567             /* yes, so add to list */
568             curr=this->files;
569             while(curr!=NULL)
570             {
571                 if (strcmp(curr->path,ditem_path) == 0) {
572                     amfree(ditem_path);
573                     return 1;
574                 }
575                 curr=curr->next;
576             }
577             that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
578             that->path = stralloc(ditem_path);
579             that->next = this->files;
580             this->files = that;         /* add at front since easiest */
581             amfree(ditem_path);
582             return 0;
583         }
584     }
585
586     /* so this is the first time we have seen this tape */
587     this = (EXTRACT_LIST *)alloc(sizeof(EXTRACT_LIST));
588     this->tape = stralloc(ditem->tape);
589     this->level = ditem->level;
590     this->fileno = ditem->fileno;
591     this->date = stralloc(ditem->date);
592     that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
593     that->path = stralloc(ditem_path);
594     that->next = NULL;
595     this->files = that;
596
597     /* add this in date increasing order          */
598     /* because restore must be done in this order */
599     /* add at begining */
600     if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0) 
601     {
602         this->next = extract_list;
603         extract_list = this;
604         amfree(ditem_path);
605         return 0;
606     }
607     for (this1 = extract_list; this1->next != NULL; this1 = this1->next)
608     {
609         /* add in the middle */
610         if(strcmp(this->date,this1->next->date) < 0)
611         {
612             this->next = this1->next;
613             this1->next = this;
614             amfree(ditem_path);
615             return 0;
616         }
617     }
618     /* add at end */
619     this->next = NULL;
620     this1->next = this;
621     amfree(ditem_path);
622     return 0;
623 }
624
625
626 /* returns -1 if error */
627 /* returns  0 on deletion */
628 /* returns  1 if not there */
629 static int
630 delete_extract_item(
631     DIR_ITEM *ditem)
632 {
633     EXTRACT_LIST *this;
634     EXTRACT_LIST_ITEM *that, *prev;
635     char *ditem_path = NULL;
636
637     ditem_path = stralloc(ditem->path);
638     clean_pathname(ditem_path);
639
640     for (this = extract_list; this != NULL; this = this->next)
641     {
642         /* see if this is the list for the tape */      
643         if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
644         {
645             /* yes, so find file on list */
646             that = this->files;
647             if (strcmp(that->path, ditem_path) == 0)
648             {
649                 /* first on list */
650                 this->files = that->next;
651                 amfree(that->path);
652                 amfree(that);
653                 /* if list empty delete it */
654                 if (this->files == NULL)
655                     delete_tape_list(this);
656                 amfree(ditem_path);
657                 return 0;
658             }
659             prev = that;
660             that = that->next;
661             while (that != NULL)
662             {
663                 if (strcmp(that->path, ditem_path) == 0)
664                 {
665                     prev->next = that->next;
666                     amfree(that->path);
667                     amfree(that);
668                     amfree(ditem_path);
669                     return 0;
670                 }
671                 prev = that;
672                 that = that->next;
673             }
674             amfree(ditem_path);
675             return 1;
676         }
677     }
678
679     amfree(ditem_path);
680     return 1;
681 }
682
683
684 void
685 add_glob(
686     char *      glob)
687 {
688     char *regex;
689     char *regex_path;
690     char *s;
691     char *uqglob = unquote_string(glob);
692  
693     regex = glob_to_regex(uqglob);
694     dbprintf(("add_glob (%s) -> %s\n", uqglob, regex));
695     if ((s = validate_regexp(regex)) != NULL) {
696         printf("%s is not a valid shell wildcard pattern: ", glob);
697         puts(s);
698     } else {
699         /*
700          * glob_to_regex() anchors the beginning of the pattern with ^,
701          * but we will be tacking it onto the end of the current directory
702          * in add_file, so strip that off.  Also, it anchors the end with
703          * $, but we need to match an optional trailing /, so tack that on
704          * the end.
705          */
706         regex_path = stralloc(regex + 1);
707         regex_path[strlen(regex_path) - 1] = '\0';
708         strappend(regex_path, "[/]*$");
709         add_file(uqglob, regex_path);
710         amfree(regex_path);
711     }
712     amfree(regex);
713     amfree(uqglob);
714 }
715
716 void
717 add_regex(
718     char *      regex)
719 {
720     char *s;
721     char *uqregex = unquote_string(regex);
722  
723     if ((s = validate_regexp(uqregex)) != NULL) {
724         printf("\"%s\" is not a valid regular expression: ", regex);
725         puts(s);
726     } else {
727         add_file(uqregex, regex);
728     }
729     amfree(uqregex);
730 }
731
732 void
733 add_file(
734     char *      path,
735     char *      regex)
736 {
737     DIR_ITEM *ditem, lditem;
738     char *path_on_disk = NULL;
739     char *path_on_disk_slash = NULL;
740     char *cmd = NULL;
741     char *err = NULL;
742     int i;
743     ssize_t j;
744     char *dir, *dir_undo, dir_undo_ch = '\0';
745     char *ditem_path = NULL;
746     char *l = NULL;
747     int  added;
748     char *s, *fp, *quoted;
749     int ch;
750     int found_one;
751     int dir_entries;
752
753     if (disk_path == NULL) {
754         printf("Must select directory before adding files\n");
755         return;
756     }
757     memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
758
759     dbprintf(("add_file: Looking for \"%s\"\n", regex));
760
761     if(strcmp(regex, "/[/]*$") == 0) {  /* "/" behave like "." */
762         regex = "\\.[/]*$";
763     }
764     else if(strcmp(regex, "[^/]*[/]*$") == 0) {         /* "*" */
765         //regex = 
766         regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
767     } else {
768         /* remove "/" at end of path */
769         j = (ssize_t)(strlen(regex) - 1);
770         while(j >= 0 && regex[j] == '/')
771             regex[j--] = '\0';
772     }
773
774     /* convert path (assumed in cwd) to one on disk */
775     if (strcmp(disk_path, "/") == 0) {
776         if (*regex == '/') {
777             /* No mods needed if already starts with '/' */
778             path_on_disk = stralloc(regex);
779         } else {
780             /* Prepend '/' */
781             path_on_disk = stralloc2("/", regex);
782         }
783     } else {
784         char *clean_disk_path = clean_regex(disk_path);
785         path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
786         amfree(clean_disk_path);
787     }
788
789     path_on_disk_slash = stralloc2(path_on_disk, "/");
790
791     dbprintf(("add_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n",
792               regex, path_on_disk));
793
794     found_one = 0;
795     dir_entries = 0;
796     for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
797     {
798         dir_entries++;
799         quoted = quote_string(ditem->path);
800         dbprintf(("add_file: Pondering ditem->path=%s\n", quoted));
801         amfree(quoted);
802         if (match(path_on_disk, ditem->path)
803             || match(path_on_disk_slash, ditem->path))
804         {
805             found_one = 1;
806             j = (ssize_t)strlen(ditem->path);
807             if((j > 0 && ditem->path[j-1] == '/')
808                || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
809             {   /* It is a directory */
810                 ditem_path = newstralloc(ditem_path, ditem->path);
811                 clean_pathname(ditem_path);
812
813                 cmd = stralloc2("ORLD ", ditem_path);
814                 if(send_command(cmd) == -1) {
815                     amfree(cmd);
816                     amfree(ditem_path);
817                     amfree(path_on_disk);
818                     amfree(path_on_disk_slash);
819                     exit(1);
820                 }
821                 amfree(cmd);
822                 cmd = NULL;
823                 /* skip preamble */
824                 if ((i = get_reply_line()) == -1) {
825                     amfree(ditem_path);
826                     amfree(path_on_disk);
827                     amfree(path_on_disk_slash);
828                     exit(1);
829                 }
830                 if(i==0) {              /* assume something wrong */
831                     amfree(ditem_path);
832                     amfree(path_on_disk);
833                     amfree(path_on_disk_slash);
834                     l = reply_line();
835                     printf("%s\n", l);
836                     return;
837                 }
838                 dir_undo = NULL;
839                 added=0;
840                 lditem.path = newstralloc(lditem.path, ditem->path);
841                 /* skip the last line -- duplicate of the preamble */
842
843                 while ((i = get_reply_line()) != 0) {
844                     if (i == -1) {
845                         amfree(ditem_path);
846                         amfree(path_on_disk);
847                         amfree(path_on_disk_slash);
848                         exit(1);
849                     }
850                     if(err) {
851                         if(cmd == NULL) {
852                             if(dir_undo) *dir_undo = dir_undo_ch;
853                             dir_undo = NULL;
854                             cmd = stralloc(l);  /* save for error report */
855                         }
856                         continue;       /* throw the rest of the lines away */
857                     }
858                     l=reply_line();
859                     if (!server_happy()) {
860                         puts(l);
861                         continue;
862                     }
863 #define sc "201-"
864                     if(strncmp(l, sc, sizeof(sc)-1) != 0) {
865                         err = "bad reply: not 201-";
866                         continue;
867                     }
868
869                     s = l + sizeof(sc)-1;
870                     ch = *s++;
871 #undef sc
872                     skip_whitespace(s, ch);
873                     if(ch == '\0') {
874                         err = "bad reply: missing date field";
875                         continue;
876                     }
877                     fp = s-1;
878                     skip_non_whitespace(s, ch);
879                     s[-1] = '\0';
880                     lditem.date = newstralloc(lditem.date, fp);
881                     s[-1] = (char)ch;
882
883                     skip_whitespace(s, ch);
884                     if(ch == '\0' || sscanf(s - 1, "%d", &lditem.level) != 1) {
885                         err = "bad reply: cannot parse level field";
886                         continue;
887                     }
888                     skip_integer(s, ch);
889
890                     skip_whitespace(s, ch);
891                     if(ch == '\0') {
892                         err = "bad reply: missing tape field";
893                         continue;
894                     }
895                     fp = s-1;
896                     skip_non_whitespace(s, ch);
897                     s[-1] = '\0';
898                     lditem.tape = newstralloc(lditem.tape, fp);
899                     s[-1] = (char)ch;
900
901                     if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
902                         skip_whitespace(s, ch);
903                         if(ch == '\0' || sscanf(s - 1, OFF_T_FMT,
904                                 (OFF_T_FMT_TYPE *)&lditem.fileno) != 1) {
905                             err = "bad reply: cannot parse fileno field";
906                             continue;
907                         }
908                         skip_integer(s, ch);
909                     }
910
911                     skip_whitespace(s, ch);
912                     if(ch == '\0') {
913                         err = "bad reply: missing directory field";
914                         continue;
915                     }
916                     dir = s - 1;
917                     skip_quoted_string(s, ch);
918                     dir_undo = s - 1;
919                     dir_undo_ch = *dir_undo;
920                     *dir_undo = '\0';
921
922                     switch(add_extract_item(&lditem)) {
923                     case -1:
924                         printf("System error\n");
925                         dbprintf(("add_file: (Failed) System error\n"));
926                         break;
927
928                     case  0:
929                         quoted = quote_string(lditem.path);
930                         printf("Added dir %s at date %s\n",
931                                quoted, lditem.date);
932                         dbprintf(("add_file: (Successful) Added dir %s at date %s\n",
933                                   quoted, lditem.date));
934                         amfree(quoted);
935                         added=1;
936                         break;
937
938                     case  1:
939                         break;
940                     }
941                 }
942                 if(!server_happy()) {
943                     puts(reply_line());
944                 } else if(err) {
945                     if (*err)
946                         puts(err);
947                     if (cmd)
948                         puts(cmd);
949                 } else if(added == 0) {
950                     quoted = quote_string(ditem_path);
951                     printf("dir %s already added\n", quoted);
952                     dbprintf(("add_file: dir %s already added\n", quoted));
953                     amfree(quoted);
954                 }
955             }
956             else /* It is a file */
957             {
958                 switch(add_extract_item(ditem)) {
959                 case -1:
960                     printf("System error\n");
961                     dbprintf(("add_file: (Failed) System error\n"));
962                     break;
963
964                 case  0:
965                     quoted = quote_string(ditem->path);
966                     printf("Added file %s\n", quoted);
967                     dbprintf(("add_file: (Successful) Added %s\n", quoted));
968                     amfree(quoted);
969                     break;
970
971                 case  1:
972                     quoted = quote_string(ditem->path);
973                     printf("File %s already added\n", quoted);
974                     dbprintf(("add_file: file %s already added\n", quoted));
975                     amfree(quoted);
976                 }
977             }
978         }
979     }
980
981     amfree(cmd);
982     amfree(ditem_path);
983     amfree(path_on_disk);
984     amfree(path_on_disk_slash);
985
986     amfree(lditem.path);
987     amfree(lditem.date);
988     amfree(lditem.tape);
989
990     if(! found_one) {
991         quoted = quote_string(path);
992         printf("File %s doesn't exist in directory\n", quoted);
993         dbprintf(("add_file: (Failed) File %s doesn't exist in directory\n",
994                   quoted));
995         amfree(quoted);
996     }
997 }
998
999
1000 void
1001 delete_glob(
1002     char *      glob)
1003 {
1004     char *regex;
1005     char *regex_path;
1006     char *s;
1007     char *uqglob = unquote_string(glob);
1008  
1009     regex = glob_to_regex(uqglob);
1010     dbprintf(("delete_glob (%s) -> %s\n", uqglob, regex));
1011     if ((s = validate_regexp(regex)) != NULL) {
1012         printf("\"%s\" is not a valid shell wildcard pattern: ", glob);
1013         puts(s);
1014     } else {
1015         /*
1016          * glob_to_regex() anchors the beginning of the pattern with ^,
1017          * but we will be tacking it onto the end of the current directory
1018          * in add_file, so strip that off.  Also, it anchors the end with
1019          * $, but we need to match an optional trailing /, so tack that on
1020          * the end.
1021          */
1022         regex_path = stralloc(regex + 1);
1023         regex_path[strlen(regex_path) - 1] = '\0';
1024         strappend(regex_path, "[/]*$");
1025         delete_file(uqglob, regex_path);
1026         amfree(regex_path);
1027     }
1028     amfree(regex);
1029     amfree(uqglob);
1030 }
1031
1032 void
1033 delete_regex(
1034     char *      regex)
1035 {
1036     char *s;
1037     char *uqregex = unquote_string(regex);
1038
1039     if ((s = validate_regexp(regex)) != NULL) {
1040         printf("\"%s\" is not a valid regular expression: ", regex);
1041         puts(s);
1042     } else {
1043         delete_file(uqregex, uqregex);
1044     }
1045     amfree(uqregex);
1046 }
1047
1048 void
1049 delete_file(
1050     char *      path,
1051     char *      regex)
1052 {
1053     DIR_ITEM *ditem, lditem;
1054     char *path_on_disk = NULL;
1055     char *path_on_disk_slash = NULL;
1056     char *cmd = NULL;
1057     char *err = NULL;
1058     int i;
1059     ssize_t j;
1060     char *date;
1061     char *tape, *tape_undo, tape_undo_ch = '\0';
1062     char *dir_undo, dir_undo_ch = '\0';
1063     int  level = 0;
1064     off_t fileno;
1065     char *ditem_path = NULL;
1066     char *l = NULL;
1067     int  deleted;
1068     char *s;
1069     int ch;
1070     int found_one;
1071     char *quoted;
1072
1073     if (disk_path == NULL) {
1074         printf("Must select directory before deleting files\n");
1075         return;
1076     }
1077     memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
1078
1079     dbprintf(("delete_file: Looking for \"%s\"\n", path));
1080
1081     if (strcmp(regex, "[^/]*[/]*$") == 0) {
1082         /* Looking for * find everything but single . */
1083         regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
1084     } else {
1085         /* remove "/" at end of path */
1086         j = (ssize_t)(strlen(regex) - 1);
1087         while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
1088     }
1089
1090     /* convert path (assumed in cwd) to one on disk */
1091     if (strcmp(disk_path, "/") == 0) {
1092         if (*regex == '/') {
1093             if (strcmp(regex, "/[/]*$") == 0) {
1094                 /* We want "/" to match the directory itself: "/." */
1095                 path_on_disk = stralloc("/\\.[/]*$");
1096             } else {
1097                 /* No mods needed if already starts with '/' */
1098                 path_on_disk = stralloc(regex);
1099             }
1100         } else {
1101             /* Prepend '/' */
1102             path_on_disk = stralloc2("/", regex);
1103         }
1104     } else {
1105         char *clean_disk_path = clean_regex(disk_path);
1106         path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
1107         amfree(clean_disk_path);
1108     }
1109
1110     path_on_disk_slash = stralloc2(path_on_disk, "/");
1111
1112     dbprintf(("delete_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n",
1113               regex, path_on_disk));
1114     found_one = 0;
1115     for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
1116     {
1117         quoted = quote_string(ditem->path);
1118         dbprintf(("delete_file: Pondering ditem->path=%s\n", quoted));
1119         amfree(quoted);
1120         if (match(path_on_disk, ditem->path)
1121             || match(path_on_disk_slash, ditem->path))
1122         {
1123             found_one = 1;
1124             j = (ssize_t)strlen(ditem->path);
1125             if((j > 0 && ditem->path[j-1] == '/')
1126                || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
1127             {   /* It is a directory */
1128                 ditem_path = newstralloc(ditem_path, ditem->path);
1129                 clean_pathname(ditem_path);
1130
1131                 cmd = stralloc2("ORLD ", ditem_path);
1132                 if(send_command(cmd) == -1) {
1133                     amfree(cmd);
1134                     amfree(ditem_path);
1135                     amfree(path_on_disk);
1136                     amfree(path_on_disk_slash);
1137                     exit(1);
1138                 }
1139                 amfree(cmd);
1140                 /* skip preamble */
1141                 if ((i = get_reply_line()) == -1) {
1142                     amfree(ditem_path);
1143                     amfree(path_on_disk);
1144                     amfree(path_on_disk_slash);
1145                     exit(1);
1146                 }
1147                 if(i==0)                /* assume something wrong */
1148                 {
1149                     amfree(ditem_path);
1150                     amfree(path_on_disk);
1151                     amfree(path_on_disk_slash);
1152                     l = reply_line();
1153                     printf("%s\n", l);
1154                     return;
1155                 }
1156                 deleted=0;
1157                 lditem.path = newstralloc(lditem.path, ditem->path);
1158                 amfree(cmd);
1159                 tape_undo = dir_undo = NULL;
1160                 /* skip the last line -- duplicate of the preamble */
1161                 while ((i = get_reply_line()) != 0)
1162                 {
1163                     if (i == -1) {
1164                         amfree(ditem_path);
1165                         amfree(path_on_disk);
1166                         amfree(path_on_disk_slash);
1167                         exit(1);
1168                     }
1169                     if(err) {
1170                         if(cmd == NULL) {
1171                             if(tape_undo) *tape_undo = tape_undo_ch;
1172                             if(dir_undo) *dir_undo = dir_undo_ch;
1173                             tape_undo = dir_undo = NULL;
1174                             cmd = stralloc(l);  /* save for the error report */
1175                         }
1176                         continue;       /* throw the rest of the lines away */
1177                     }
1178                     l=reply_line();
1179                     if (!server_happy()) {
1180                         puts(l);
1181                         continue;
1182                     }
1183 #define sc "201-"
1184                     if(strncmp(l, sc, sizeof(sc)-1) != 0) {
1185                         err = "bad reply: not 201-";
1186                         continue;
1187                     }
1188                     s = l + sizeof(sc)-1;
1189                     ch = *s++;
1190 #undef sc
1191                     skip_whitespace(s, ch);
1192                     if(ch == '\0') {
1193                         err = "bad reply: missing date field";
1194                         continue;
1195                     }
1196                     date = s - 1;
1197                     skip_non_whitespace(s, ch);
1198                     *(s - 1) = '\0';
1199
1200                     skip_whitespace(s, ch);
1201                     if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1202                         err = "bad reply: cannot parse level field";
1203                         continue;
1204                     }
1205                     skip_integer(s, ch);
1206
1207                     skip_whitespace(s, ch);
1208                     if(ch == '\0') {
1209                         err = "bad reply: missing tape field";
1210                         continue;
1211                     }
1212                     tape = s - 1;
1213                     skip_non_whitespace(s, ch);
1214                     tape_undo = s - 1;
1215                     tape_undo_ch = *tape_undo;
1216                     *tape_undo = '\0';
1217
1218                     if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
1219                         skip_whitespace(s, ch);
1220                         if(ch == '\0' || sscanf(s - 1, OFF_T_FMT,
1221                                 (OFF_T_FMT_TYPE *)&fileno) != 1) {
1222                             err = "bad reply: cannot parse fileno field";
1223                             continue;
1224                         }
1225                         skip_integer(s, ch);
1226                     }
1227
1228                     skip_whitespace(s, ch);
1229                     if(ch == '\0') {
1230                         err = "bad reply: missing directory field";
1231                         continue;
1232                     }
1233                     skip_non_whitespace(s, ch);
1234                     dir_undo = s - 1;
1235                     dir_undo_ch = *dir_undo;
1236                     *dir_undo = '\0';
1237
1238                     lditem.date = newstralloc(lditem.date, date);
1239                     lditem.level=level;
1240                     lditem.tape = newstralloc(lditem.tape, tape);
1241                     switch(delete_extract_item(&lditem)) {
1242                     case -1:
1243                         printf("System error\n");
1244                         dbprintf(("delete_file: (Failed) System error\n"));
1245                         break;
1246                     case  0:
1247                         printf("Deleted dir %s at date %s\n", ditem_path, date);
1248                         dbprintf(("delete_file: (Successful) Deleted dir %s at date %s\n",
1249                                   ditem_path, date));
1250                         deleted=1;
1251                         break;
1252                     case  1:
1253                         break;
1254                     }
1255                 }
1256                 if(!server_happy()) {
1257                     puts(reply_line());
1258                 } else if(err) {
1259                     if (*err)
1260                         puts(err);
1261                     if (cmd)
1262                         puts(cmd);
1263                 } else if(deleted == 0) {
1264                     printf("Warning - dir '%s' not on tape list\n",
1265                            ditem_path);
1266                     dbprintf(("delete_file: dir '%s' not on tape list\n",
1267                               ditem_path));
1268                 }
1269             }
1270             else
1271             {
1272                 switch(delete_extract_item(ditem)) {
1273                 case -1:
1274                     printf("System error\n");
1275                     dbprintf(("delete_file: (Failed) System error\n"));
1276                     break;
1277                 case  0:
1278                     printf("Deleted %s\n", ditem->path);
1279                     dbprintf(("delete_file: (Successful) Deleted %s\n",
1280                               ditem->path));
1281                     break;
1282                 case  1:
1283                     printf("Warning - file '%s' not on tape list\n",
1284                            ditem->path);
1285                     dbprintf(("delete_file: file '%s' not on tape list\n",
1286                               ditem->path));
1287                     break;
1288                 }
1289             }
1290         }
1291     }
1292     amfree(cmd);
1293     amfree(ditem_path);
1294     amfree(path_on_disk);
1295     amfree(path_on_disk_slash);
1296
1297     if(! found_one) {
1298         printf("File %s doesn't exist in directory\n", path);
1299         dbprintf(("delete_file: (Failed) File %s doesn't exist in directory\n",
1300                   path));
1301     }
1302 }
1303
1304
1305 /* print extract list into file. If NULL ptr passed print to screen */
1306 void
1307 display_extract_list(
1308     char *      file)
1309 {
1310     EXTRACT_LIST *this;
1311     EXTRACT_LIST_ITEM *that;
1312     FILE *fp;
1313     char *pager;
1314     char *pager_command;
1315     char *uqfile;
1316
1317     if (file == NULL)
1318     {
1319         if ((pager = getenv("PAGER")) == NULL)
1320         {
1321             pager = "more";
1322         }
1323         /*
1324          * Set up the pager command so if the pager is terminated, we do
1325          * not get a SIGPIPE back.
1326          */
1327         pager_command = stralloc2(pager, " ; /bin/cat > /dev/null");
1328         if ((fp = popen(pager_command, "w")) == NULL)
1329         {
1330             printf("Warning - can't pipe through %s\n", pager);
1331             fp = stdout;
1332         }
1333         amfree(pager_command);
1334     }
1335     else
1336     {
1337         uqfile = unquote_string(file);
1338         if ((fp = fopen(uqfile, "w")) == NULL)
1339         {
1340             printf("Can't open file %s to print extract list into\n", file);
1341             amfree(uqfile);
1342             return;
1343         }
1344         amfree(uqfile);
1345     }
1346
1347     for (this = extract_list; this != NULL; this = this->next)
1348     {
1349         fprintf(fp, "TAPE %s LEVEL %d DATE %s\n",
1350                 this->tape, this->level, this->date);
1351         for (that = this->files; that != NULL; that = that->next)
1352             fprintf(fp, "\t%s\n", that->path);
1353     }
1354
1355     if (file == NULL) {
1356         apclose(fp);
1357     } else {
1358         printf("Extract list written to file %s\n", file);
1359         afclose(fp);
1360     }
1361 }
1362
1363
1364 static int
1365 is_empty_dir(
1366     char *fname)
1367 {
1368     DIR *dir;
1369     struct dirent *entry;
1370     int gotentry;
1371
1372     if((dir = opendir(fname)) == NULL)
1373         return 1;
1374
1375     gotentry = 0;
1376     while(!gotentry && (entry = readdir(dir)) != NULL) {
1377         gotentry = !is_dot_or_dotdot(entry->d_name);
1378     }
1379
1380     closedir(dir);
1381     return !gotentry;
1382
1383 }
1384
1385 /* returns 0 if extract list empty and 1 if it isn't */
1386 int
1387 is_extract_list_nonempty(void)
1388 {
1389     return (extract_list != NULL);
1390 }
1391
1392
1393 /* prints continue prompt and waits for response,
1394    returns 0 if don't, non-0 if do */
1395 static int
1396 okay_to_continue(
1397     int allow_tape,
1398     int allow_skip,
1399     int allow_retry)
1400 {
1401     int ch;
1402     int ret = -1;
1403     char *line = NULL;
1404     char *s;
1405     char *prompt;
1406     int get_tape;
1407
1408     get_tape = 0;
1409     while (ret < 0) {
1410         if (get_tape) {
1411             prompt = "New tape device [?]: ";
1412         } else if (allow_tape && allow_skip) {
1413             prompt = "Continue [?/Y/n/s/t]? ";
1414         } else if (allow_tape && !allow_skip) {
1415             prompt = "Continue [?/Y/n/t]? ";
1416         } else if (allow_retry) {
1417             prompt = "Continue [?/Y/n/r]? ";
1418         } else {
1419             prompt = "Continue [?/Y/n]? ";
1420         }
1421         fputs(prompt, stdout);
1422         fflush(stdout); fflush(stderr);
1423         amfree(line);
1424         if ((line = agets(stdin)) == NULL) {
1425             putchar('\n');
1426             clearerr(stdin);
1427             if (get_tape) {
1428                 get_tape = 0;
1429                 continue;
1430             }
1431             ret = 0;
1432             break;
1433         }
1434         s = line;
1435         while ((ch = *s++) != '\0' && isspace(ch)) {
1436             (void)ch;   /* Quiet empty loop compiler warning */
1437         }
1438         if (ch == '?') {
1439             if (get_tape) {
1440                 printf("Enter a new device ([host:]device) or \"default\"\n");
1441             } else {
1442                 printf("Enter \"y\"es to continue, \"n\"o to stop");
1443                 if(allow_skip) {
1444                     printf(", \"s\"kip this tape");
1445                 }
1446                 if(allow_retry) {
1447                     printf(" or \"r\"etry this tape");
1448                 }
1449                 if (allow_tape) {
1450                     printf(" or \"t\"ape to change tape drives");
1451                 }
1452                 putchar('\n');
1453             }
1454         } else if (get_tape) {
1455             set_tape(s - 1);
1456             get_tape = 0;
1457         } else if (ch == '\0' || ch == 'Y' || ch == 'y') {
1458             ret = 1;
1459         } else if (allow_tape && (ch == 'T' || ch == 't')) {
1460             get_tape = 1;
1461         } else if (ch == 'N' || ch == 'n') {
1462             ret = 0;
1463         } else if (allow_retry && (ch == 'R' || ch == 'r')) {
1464             ret = RETRY_TAPE;
1465         } else if (allow_skip && (ch == 'S' || ch == 's')) {
1466             ret = SKIP_TAPE;
1467         }
1468     }
1469     /*@ignore@*/
1470     amfree(line);
1471     /*@end@*/
1472     return ret;
1473 }
1474
1475 static void
1476 send_to_tape_server(
1477     security_stream_t * stream,
1478     char *              cmd)
1479 {
1480     char *msg = stralloc2(cmd, "\r\n");
1481
1482     if (security_stream_write(stream, msg, strlen(msg)) < 0)
1483     {
1484         error("Error writing to tape server");
1485         exit(101);
1486         /*NOTREACHED*/
1487     }
1488     amfree(msg);
1489 }
1490
1491
1492 /* start up connection to tape server and set commands to initiate
1493    transfer of dump image.
1494    Return tape server socket on success, -1 on error. */
1495 static int
1496 extract_files_setup(
1497     char *      label,
1498     off_t       fsf)
1499 {
1500     char *disk_regex = NULL;
1501     char *host_regex = NULL;
1502     char *clean_datestamp, *ch, *ch1;
1503     char *tt = NULL;
1504     char *req;
1505     int response_error;
1506
1507     amidxtaped_secdrv = security_getdriver(authopt);
1508     if (amidxtaped_secdrv == NULL) {
1509         error("no '%s' security driver available for host '%s'",
1510               authopt, tape_server_name);
1511     }
1512
1513     /* We assume that amidxtaped support fe_amidxtaped_options_features */
1514     /*                               and fe_amidxtaped_options_auth     */
1515     /* We should send a noop to really know                             */
1516     req = vstralloc("SERVICE amidxtaped\n",
1517                     "OPTIONS ", "features=", our_features_string, ";",
1518                                 "auth=", authopt, ";",
1519                     "\n", NULL);
1520     protocol_sendreq(tape_server_name, amidxtaped_secdrv,
1521                      amidxtaped_client_get_security_conf, req, STARTUP_TIMEOUT,
1522                      amidxtaped_response, &response_error);
1523     amfree(req);
1524     protocol_run();
1525     if(response_error != 0) {
1526         return -1;
1527     }
1528
1529     disk_regex = alloc(strlen(disk_name) * 2 + 3);
1530
1531     ch = disk_name;
1532     ch1 = disk_regex;
1533
1534     /* we want to force amrestore to only match disk_name exactly */
1535     *(ch1++) = '^';
1536
1537     /* We need to escape some characters first... NT compatibilty crap */
1538     for (; *ch != 0; ch++, ch1++) {
1539         switch (*ch) {     /* done this way in case there are more */
1540         case '$':
1541             *(ch1++) = '\\';
1542             /* no break; we do want to fall through... */
1543         default:
1544             *ch1 = *ch;
1545         }
1546     }
1547
1548     /* we want to force amrestore to only match disk_name exactly */
1549     *(ch1++) = '$';
1550
1551     *ch1 = '\0';
1552
1553     host_regex = alloc(strlen(dump_hostname) * 2 + 3);
1554
1555     ch = dump_hostname;
1556     ch1 = host_regex;
1557
1558     /* we want to force amrestore to only match dump_hostname exactly */
1559     *(ch1++) = '^';
1560
1561     /* We need to escape some characters first... NT compatibilty crap */
1562     for (; *ch != 0; ch++, ch1++) {
1563         switch (*ch) {     /* done this way in case there are more */
1564         case '$':
1565             *(ch1++) = '\\';
1566             /* no break; we do want to fall through... */
1567         default:
1568             *ch1 = *ch;
1569         }
1570     }
1571
1572     /* we want to force amrestore to only match dump_hostname exactly */
1573     *(ch1++) = '$';
1574
1575     *ch1 = '\0';
1576
1577     clean_datestamp = stralloc(dump_datestamp);
1578     for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) {
1579         if(*ch1 != '-') {
1580             *ch = *ch1;
1581             ch++;
1582         }
1583     }
1584     *ch = '\0';
1585     /* push our feature list off to the tape server */
1586     /* XXX assumes that index server and tape server are equivalent, ew */
1587
1588     if(am_has_feature(indexsrv_features, fe_amidxtaped_exchange_features)){
1589         tt = newstralloc2(tt, "FEATURES=", our_features_string);
1590         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1591         get_amidxtaped_line();
1592         if(strncmp(amidxtaped_line,"FEATURES=",9) == 0) {
1593             tapesrv_features = am_string_to_feature(amidxtaped_line+9);
1594         } else {
1595             fprintf(stderr, "amrecover - expecting FEATURES line from amidxtaped\n");
1596             stop_amidxtaped();
1597             amfree(disk_regex);
1598             amfree(host_regex);
1599             amfree(clean_datestamp);
1600             return -1;
1601         }
1602         am_release_feature_set(tapesrv_features);
1603     }
1604
1605
1606     if(am_has_feature(indexsrv_features, fe_amidxtaped_header) &&
1607        am_has_feature(indexsrv_features, fe_amidxtaped_device) &&
1608        am_has_feature(indexsrv_features, fe_amidxtaped_host) &&
1609        am_has_feature(indexsrv_features, fe_amidxtaped_disk) &&
1610        am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) {
1611
1612         if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) {
1613             tt = newstralloc2(tt, "CONFIG=", config);
1614             send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1615         }
1616         if(am_has_feature(indexsrv_features, fe_amidxtaped_label) &&
1617            label && label[0] != '/') {
1618             tt = newstralloc2(tt,"LABEL=",label);
1619             send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1620         }
1621         if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) {
1622             char v_fsf[100];
1623             snprintf(v_fsf, 99, OFF_T_FMT, (OFF_T_FMT_TYPE)fsf);
1624             tt = newstralloc2(tt, "FSF=",v_fsf);
1625             send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1626         }
1627         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "HEADER");
1628         tt = newstralloc2(tt, "DEVICE=", dump_device_name);
1629         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1630         tt = newstralloc2(tt, "HOST=", host_regex);
1631         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1632         tt = newstralloc2(tt, "DISK=", disk_regex);
1633         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1634         tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp);
1635         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1636         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "END");
1637         amfree(tt);
1638     }
1639     else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) {
1640         /* send to the tape server what tape file we want */
1641         /* 6 args:
1642          *   "-h"
1643          *   "-p"
1644          *   "tape device"
1645          *   "hostname"
1646          *   "diskname"
1647          *   "datestamp"
1648          */
1649         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "6");
1650         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "-h");
1651         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "-p");
1652         send_to_tape_server(amidxtaped_streams[CTLFD].fd, dump_device_name);
1653         send_to_tape_server(amidxtaped_streams[CTLFD].fd, host_regex);
1654         send_to_tape_server(amidxtaped_streams[CTLFD].fd, disk_regex);
1655         send_to_tape_server(amidxtaped_streams[CTLFD].fd, clean_datestamp);
1656
1657         dbprintf(("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n",
1658                   dump_device_name, host_regex, disk_regex, clean_datestamp));
1659     }
1660
1661     amfree(disk_regex);
1662     amfree(host_regex);
1663     amfree(clean_datestamp);
1664
1665     return 0;
1666 }
1667
1668
1669 /*
1670  * Reads the first block of a tape file.
1671  */
1672
1673 void
1674 read_file_header(
1675     char *      buffer,
1676     dumpfile_t *file,
1677     size_t      buflen,
1678     int         tapedev)
1679 {
1680     ssize_t bytes_read;
1681     bytes_read = read_buffer(tapedev, buffer, buflen, READ_TIMEOUT);
1682     if(bytes_read < 0) {
1683         error("error reading header (%s), check amidxtaped.*.debug on server",
1684               strerror(errno));
1685         /*NOTREACHED*/
1686     }
1687
1688     if((size_t)bytes_read < buflen) {
1689         fprintf(stderr, "%s: short block %d byte%s\n",
1690                 get_pname(), (int)bytes_read, (bytes_read == 1) ? "" : "s");
1691         print_header(stdout, file);
1692         error("Can't read file header");
1693         /*NOTREACHED*/
1694     }
1695
1696     /* bytes_read == buflen */
1697     parse_file_header(buffer, file, (size_t)bytes_read);
1698 }
1699
1700 enum dumptypes {
1701         IS_UNKNOWN,
1702         IS_DUMP,
1703         IS_GNUTAR,
1704         IS_TAR,
1705         IS_SAMBA,
1706         IS_SAMBA_TAR
1707 };
1708
1709 static void
1710 extract_files_child(
1711     int                 in_fd,
1712     EXTRACT_LIST *      elist)
1713 {
1714     int save_errno;
1715     int extra_params = 0;
1716     int i,j=0;
1717     char **restore_args = NULL;
1718     int files_off_tape;
1719     EXTRACT_LIST_ITEM *fn;
1720     enum dumptypes dumptype = IS_UNKNOWN;
1721     char buffer[DISK_BLOCK_BYTES];
1722     dumpfile_t file;
1723     size_t len_program;
1724     char *cmd = NULL;
1725     int passwd_field = -1;
1726 #ifdef SAMBA_CLIENT
1727     char *domain = NULL, *smbpass = NULL;
1728 #endif
1729
1730     /* code executed by child to do extraction */
1731     /* never returns */
1732
1733     /* make in_fd be our stdin */
1734     if (dup2(in_fd, STDIN_FILENO) == -1)
1735     {
1736         error("dup2 failed in extract_files_child: %s", strerror(errno));
1737         /*NOTREACHED*/
1738     }
1739
1740     /* read the file header */
1741     fh_init(&file);
1742     read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO);
1743
1744     if(file.type != F_DUMPFILE) {
1745         print_header(stdout, &file);
1746         error("bad header");
1747         /*NOTREACHED*/
1748     }
1749
1750     if (file.program != NULL) {
1751 #ifdef GNUTAR
1752         if (strcmp(file.program, GNUTAR) == 0)
1753             dumptype = IS_GNUTAR;
1754 #endif
1755
1756         if (dumptype == IS_UNKNOWN) {
1757             len_program = strlen(file.program);
1758             if(len_program >= 3 &&
1759                strcmp(&file.program[len_program-3],"tar") == 0)
1760                 dumptype = IS_TAR;
1761         }
1762
1763 #ifdef SAMBA_CLIENT
1764         if (dumptype == IS_UNKNOWN && strcmp(file.program, SAMBA_CLIENT) ==0) {
1765             if (samba_extract_method == SAMBA_TAR)
1766               dumptype = IS_SAMBA_TAR;
1767             else
1768               dumptype = IS_SAMBA;
1769         }
1770 #endif
1771     }
1772
1773     /* form the arguments to restore */
1774     files_off_tape = length_of_tape_list(elist);
1775     switch (dumptype) {
1776     case IS_SAMBA:
1777 #ifdef SAMBA_CLIENT
1778         extra_params = 10;
1779         break;
1780 #endif
1781     case IS_TAR:
1782     case IS_GNUTAR:
1783         extra_params = 4;
1784         break;
1785     case IS_SAMBA_TAR:
1786         extra_params = 3;
1787         break;
1788     case IS_UNKNOWN:
1789     case IS_DUMP:
1790 #ifdef AIX_BACKUP
1791         extra_params = 2;
1792 #else
1793 #if defined(XFSDUMP)
1794         if (strcmp(file.program, XFSDUMP) == 0) {
1795             extra_params = 4 + files_off_tape;
1796         } else
1797 #endif
1798         {
1799         extra_params = 4;
1800         }
1801 #endif
1802         break;
1803     }
1804
1805     restore_args = (char **)alloc((size_t)((extra_params + files_off_tape + 1)
1806                                   * sizeof(char *)));
1807     switch(dumptype) {
1808     case IS_SAMBA:
1809 #ifdef SAMBA_CLIENT
1810         restore_args[j++] = stralloc("smbclient");
1811         smbpass = findpass(file.disk, &domain);
1812         if (smbpass) {
1813             restore_args[j++] = stralloc(file.disk);
1814             passwd_field=j;
1815             restore_args[j++] = stralloc("-U");
1816             restore_args[j++] = smbpass;
1817             if (domain) {
1818                 restore_args[j++] = stralloc("-W");
1819                 restore_args[j++] = stralloc(domain);
1820             } else
1821                 extra_params -= 2;
1822         } else
1823             extra_params -= 6;
1824         restore_args[j++] = stralloc("-d0");
1825         restore_args[j++] = stralloc("-Tx");
1826         restore_args[j++] = stralloc("-");      /* data on stdin */
1827         break;
1828 #endif
1829     case IS_TAR:
1830     case IS_GNUTAR:
1831         restore_args[j++] = stralloc("tar");
1832         restore_args[j++] = stralloc("--numeric-owner");
1833         restore_args[j++] = stralloc("-xpGvf");
1834         restore_args[j++] = stralloc("-");      /* data on stdin */
1835         break;
1836     case IS_SAMBA_TAR:
1837         restore_args[j++] = stralloc("tar");
1838         restore_args[j++] = stralloc("-xpvf");
1839         restore_args[j++] = stralloc("-");      /* data on stdin */
1840         break;
1841     case IS_UNKNOWN:
1842     case IS_DUMP:
1843         restore_args[j++] = stralloc("restore");
1844 #ifdef AIX_BACKUP
1845         restore_args[j++] = stralloc("-xB");
1846 #else
1847 #if defined(XFSDUMP)
1848         if (strcmp(file.program, XFSDUMP) == 0) {
1849             restore_args[j++] = stralloc("-v");
1850             restore_args[j++] = stralloc("silent");
1851         } else
1852 #endif
1853 #if defined(VDUMP)
1854         if (strcmp(file.program, VDUMP) == 0) {
1855             restore_args[j++] = stralloc("xf");
1856             restore_args[j++] = stralloc("-");  /* data on stdin */
1857         } else
1858 #endif
1859         {
1860         restore_args[j++] = stralloc("xbf");
1861         restore_args[j++] = stralloc("2");      /* read in units of 1K */
1862         restore_args[j++] = stralloc("-");      /* data on stdin */
1863         }
1864 #endif
1865     }
1866   
1867     for (i = 0, fn = elist->files; i < files_off_tape; i++, fn = fn->next)
1868     {
1869         switch (dumptype) {
1870         case IS_TAR:
1871         case IS_GNUTAR:
1872         case IS_SAMBA_TAR:
1873         case IS_SAMBA:
1874             if (strcmp(fn->path, "/") == 0)
1875                 restore_args[j++] = stralloc(".");
1876             else
1877                 restore_args[j++] = stralloc2(".", fn->path);
1878             break;
1879         case IS_UNKNOWN:
1880         case IS_DUMP:
1881 #if defined(XFSDUMP)
1882             if (strcmp(file.program, XFSDUMP) == 0) {
1883                 /*
1884                  * xfsrestore needs a -s option before each file to be
1885                  * restored, and also wants them to be relative paths.
1886                  */
1887                 restore_args[j++] = stralloc("-s");
1888                 restore_args[j++] = stralloc(fn->path + 1);
1889             } else
1890 #endif
1891             {
1892             restore_args[j++] = stralloc(fn->path);
1893             }
1894         }
1895     }
1896 #if defined(XFSDUMP)
1897     if (strcmp(file.program, XFSDUMP) == 0) {
1898         restore_args[j++] = stralloc("-");
1899         restore_args[j++] = stralloc(".");
1900     }
1901 #endif
1902     restore_args[j] = NULL;
1903
1904     switch (dumptype) {
1905     case IS_SAMBA:
1906 #ifdef SAMBA_CLIENT
1907         cmd = stralloc(SAMBA_CLIENT);
1908         break;
1909 #else
1910         /* fall through to ... */
1911 #endif
1912     case IS_TAR:
1913     case IS_GNUTAR:
1914     case IS_SAMBA_TAR:
1915 #ifndef GNUTAR
1916         fprintf(stderr, "warning: GNUTAR program not available.\n");
1917         cmd = stralloc("tar");
1918 #else
1919         cmd = stralloc(GNUTAR);
1920 #endif
1921         break;
1922     case IS_UNKNOWN:
1923     case IS_DUMP:
1924         cmd = NULL;
1925 #if defined(DUMP)
1926         if (strcmp(file.program, DUMP) == 0) {
1927             cmd = stralloc(RESTORE);
1928         }
1929 #endif
1930 #if defined(VDUMP)
1931         if (strcmp(file.program, VDUMP) == 0) {
1932             cmd = stralloc(VRESTORE);
1933         }
1934 #endif
1935 #if defined(VXDUMP)
1936         if (strcmp(file.program, VXDUMP) == 0) {
1937             cmd = stralloc(VXRESTORE);
1938         }
1939 #endif
1940 #if defined(XFSDUMP)
1941         if (strcmp(file.program, XFSDUMP) == 0) {
1942             cmd = stralloc(XFSRESTORE);
1943         }
1944 #endif
1945         if (cmd == NULL) {
1946             fprintf(stderr, "warning: restore program for %s not available.\n",
1947                     file.program);
1948             cmd = stralloc("restore");
1949         }
1950     }
1951     if (cmd) {
1952         dbprintf(("Exec'ing %s with arguments:\n", cmd));
1953         for (i = 0; i < j; i++) {
1954             if( i == passwd_field)
1955                 dbprintf(("\tXXXXX\n"));
1956             else
1957                 dbprintf(("\t%s\n", restore_args[i]));
1958         }
1959         (void)execv(cmd, restore_args);
1960         /* only get here if exec failed */
1961         save_errno = errno;
1962         for (i = 0; i < j; i++) {
1963             amfree(restore_args[i]);
1964         }
1965         amfree(restore_args);
1966         errno = save_errno;
1967         perror("amrecover couldn't exec");
1968         fprintf(stderr, " problem executing %s\n", cmd);
1969         amfree(cmd);
1970     }
1971     exit(1);
1972     /*NOT REACHED */
1973 }
1974
1975 /*
1976  * Interpose something between the process writing out the dump (writing it to
1977  * some extraction program, really) and the socket from which we're reading, so
1978  * that we can do things like prompt for human interaction for multiple tapes.
1979  */
1980 int
1981 writer_intermediary(
1982     EXTRACT_LIST *      elist)
1983 {
1984     int child_pipe[2];
1985     pid_t pid;
1986     amwait_t extractor_status;
1987
1988     if(pipe(child_pipe) == -1) {
1989         error("extract_list - error setting up pipe to extractor: %s\n",
1990               strerror(errno));
1991         /*NOTREACHED*/
1992     }
1993
1994     /* okay, ready to extract. fork a child to do the actual work */
1995     if ((pid = fork()) == 0) {
1996         /* this is the child process */
1997         /* never gets out of this clause */
1998         aclose(child_pipe[1]);
1999         extract_files_child(child_pipe[0], elist);
2000         /*NOTREACHED*/
2001     }
2002
2003     /* This is the parent */
2004     if (pid == -1) {
2005         printf("writer_intermediary - error forking child");
2006         return -1;
2007     }
2008
2009     aclose(child_pipe[0]);
2010
2011     security_stream_read(amidxtaped_streams[DATAFD].fd,
2012                          read_amidxtaped_data, &(child_pipe[1]));
2013
2014     while(get_amidxtaped_line() >= 0) {
2015         char desired_tape[MAX_TAPE_LABEL_BUF];
2016                 
2017         /* if prompted for a tape, relay said prompt to the user */
2018         if(sscanf(amidxtaped_line, "FEEDME %132s\n", desired_tape) == 1) {
2019             int done;
2020             printf("Load tape %s now\n", desired_tape);
2021             done = okay_to_continue(am_has_feature(indexsrv_features,
2022                                                    fe_amrecover_feedme_tape),
2023                                     0, 0);
2024             if (done == 1) {
2025                 if (am_has_feature(indexsrv_features,
2026                                    fe_amrecover_feedme_tape)) {
2027                     char *reply = stralloc2("TAPE ", tape_device_name);
2028                     send_to_tape_server(amidxtaped_streams[CTLFD].fd, reply);
2029                     amfree(reply);
2030                 } else {
2031                     send_to_tape_server(amidxtaped_streams[CTLFD].fd, "OK");
2032                 }
2033             } else {
2034                 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "ERROR");
2035                 break;
2036             }
2037         } else if(strncmp(amidxtaped_line, "MESSAGE ", 8) == 0) {
2038             printf("%s\n",&amidxtaped_line[8]);
2039         } else {
2040             fprintf(stderr, "Strange message from tape server: %s",
2041                     amidxtaped_line);
2042             break;
2043         }
2044     }
2045
2046     /* CTL might be close before DATA */
2047     event_loop(0);
2048     aclose(child_pipe[1]);
2049
2050     waitpid(pid, &extractor_status, 0);
2051     if(WEXITSTATUS(extractor_status) != 0){
2052         int ret = WEXITSTATUS(extractor_status);
2053         if(ret == 255) ret = -1;
2054         printf("Extractor child exited with status %d\n", ret);
2055         return -1;
2056     }
2057     return(0);
2058 }
2059
2060 /* exec restore to do the actual restoration */
2061
2062 /* does the actual extraction of files */
2063 /*
2064  * The original design had the dump image being returned exactly as it
2065  * appears on the tape, and this routine getting from the index server
2066  * whether or not it is compressed, on the assumption that the tape
2067  * server may not know how to uncompress it. But
2068  * - Amrestore can't do that. It returns either compressed or uncompressed
2069  * (always). Amrestore assumes it can uncompress files. It is thus a good
2070  * idea to run the tape server on a machine with gzip.
2071  * - The information about compression in the disklist is really only
2072  * for future dumps. It is possible to change compression on a drive
2073  * so the information in the disklist may not necessarily relate to
2074  * the dump image on the tape.
2075  *   Consequently the design was changed to assuming that amrestore can
2076  * uncompress any dump image and have it return an uncompressed file
2077  * always.
2078  */
2079 void
2080 extract_files(void)
2081 {
2082     EXTRACT_LIST *elist;
2083     char cwd[STR_SIZE];
2084     char *l;
2085     int first;
2086     int otc;
2087     tapelist_t *tlist = NULL, *a_tlist;
2088
2089     if (!is_extract_list_nonempty())
2090     {
2091         printf("Extract list empty - No files to extract!\n");
2092         return;
2093     }
2094
2095     clean_extract_list();
2096
2097     /* get tape device name from index server if none specified */
2098     if (tape_server_name == NULL) {
2099         tape_server_name = newstralloc(tape_server_name, server_name);
2100     }
2101     if (tape_device_name == NULL) {
2102         if (send_command("TAPE") == -1)
2103             exit(1);
2104         if (get_reply_line() == -1)
2105             exit(1);
2106         l = reply_line();
2107         if (!server_happy())
2108         {
2109             printf("%s\n", l);
2110             exit(1);
2111         }
2112         /* skip reply number */
2113         tape_device_name = newstralloc(tape_device_name, l+4);
2114     }
2115
2116     if (strcmp(tape_device_name, "/dev/null") == 0)
2117     {
2118         printf("amrecover: warning: using %s as the tape device will not work\n",
2119                tape_device_name);
2120     }
2121
2122     first=1;
2123     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) {
2124         if(elist->tape[0]!='/') {
2125             if(first) {
2126                 printf("\nExtracting files using tape drive %s on host %s.\n",
2127                         tape_device_name, tape_server_name);
2128                 printf("The following tapes are needed:");
2129                 first=0;
2130             }
2131             else
2132                 printf("                               ");
2133             tlist = unmarshal_tapelist_str(elist->tape); 
2134             for(a_tlist = tlist ; a_tlist != NULL; a_tlist = a_tlist->next)
2135                 printf(" %s", a_tlist->label);
2136             printf("\n");
2137             free_tapelist(tlist);
2138         }
2139     }
2140     first=1;
2141     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) {
2142         if(elist->tape[0]=='/') {
2143             if(first) {
2144                 printf("\nExtracting files from holding disk on host %s.\n",
2145                         tape_server_name);
2146                 printf("The following files are needed:");
2147                 first=0;
2148             }
2149             else
2150                 printf("                               ");
2151             tlist = unmarshal_tapelist_str(elist->tape); 
2152             for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next)
2153                 printf(" %s", a_tlist->label);
2154             printf("\n");
2155             free_tapelist(tlist);
2156         }
2157     }
2158     printf("\n");
2159
2160     if (getcwd(cwd, sizeof(cwd)) == NULL) {
2161         perror("extract_list: Current working directory unavailable");
2162         exit(1);
2163     }
2164
2165     printf("Restoring files into directory %s\n", cwd);
2166     check_file_overwrite(cwd);
2167
2168 #ifdef SAMBA_CLIENT
2169     if (samba_extract_method == SAMBA_SMBCLIENT)
2170       printf("(unless it is a Samba backup, that will go through to the SMB server)\n");
2171 #endif
2172     if (!okay_to_continue(0,0,0))
2173         return;
2174     printf("\n");
2175
2176     if (!do_unlink_list()) {
2177         fprintf(stderr, "Can't recover because I can't cleanup the cwd (%s)\n",
2178                 cwd);
2179         return;
2180     }
2181     free_unlink_list();
2182
2183     while ((elist = first_tape_list()) != NULL)
2184     {
2185         if(elist->tape[0]=='/') {
2186             dump_device_name = newstralloc(dump_device_name, elist->tape);
2187             printf("Extracting from file ");
2188             tlist = unmarshal_tapelist_str(dump_device_name); 
2189             for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next)
2190                 printf(" %s", a_tlist->label);
2191             printf("\n");
2192             free_tapelist(tlist);
2193         }
2194         else {
2195             printf("Extracting files using tape drive %s on host %s.\n",
2196                    tape_device_name, tape_server_name);
2197             tlist = unmarshal_tapelist_str(elist->tape); 
2198             printf("Load tape %s now\n", tlist->label);
2199             free_tapelist(tlist);
2200             otc = okay_to_continue(1,1,0);
2201             if (otc == 0)
2202                 return;
2203             else if (otc == SKIP_TAPE) {
2204                 delete_tape_list(elist); /* skip this tape */
2205                 continue;
2206             }
2207             dump_device_name = newstralloc(dump_device_name, tape_device_name);
2208         }
2209         dump_datestamp = newstralloc(dump_datestamp, elist->date);
2210
2211         /* connect to the tape handler daemon on the tape drive server */
2212         if ((extract_files_setup(elist->tape, elist->fileno)) == -1)
2213         {
2214             fprintf(stderr, "amrecover - can't talk to tape server\n");
2215             return;
2216         }
2217
2218         /* if the server have fe_amrecover_feedme_tape, it has asked for
2219          * the tape itself, even if the restore didn't succeed, we should
2220          * remove it.
2221          */
2222         if(writer_intermediary(elist) == 0 ||
2223            am_has_feature(indexsrv_features, fe_amrecover_feedme_tape))
2224             delete_tape_list(elist);    /* tape done so delete from list */
2225
2226         stop_amidxtaped();
2227     }
2228 }
2229
2230 static void
2231 amidxtaped_response(
2232     void *              datap,
2233     pkt_t *             pkt,
2234     security_handle_t * sech)
2235 {
2236     int ports[NSTREAMS], *response_error = datap, i;
2237     char *p;
2238     char *tok;
2239     char *extra = NULL;
2240
2241     assert(response_error != NULL);
2242     assert(sech != NULL);
2243     memset(ports, -1, SIZEOF(ports));
2244
2245     security_close_connection(sech, dump_hostname);
2246     if (pkt == NULL) {
2247         errstr = newvstralloc(errstr, "[request failed: ",
2248                              security_geterror(sech), "]", NULL);
2249         *response_error = 1;
2250         return;
2251     }
2252
2253     if (pkt->type == P_NAK) {
2254 #if defined(PACKET_DEBUG)
2255         fprintf(stderr, "got nak response:\n----\n%s\n----\n\n", pkt->body);
2256 #endif
2257
2258         tok = strtok(pkt->body, " ");
2259         if (tok == NULL || strcmp(tok, "ERROR") != 0)
2260             goto bad_nak;
2261
2262         tok = strtok(NULL, "\n");
2263         if (tok != NULL) {
2264             errstr = newvstralloc(errstr, "NAK: ", tok, NULL);
2265             *response_error = 1;
2266         } else {
2267 bad_nak:
2268             errstr = newstralloc(errstr, "request NAK");
2269             *response_error = 2;
2270         }
2271         return;
2272     }
2273
2274     if (pkt->type != P_REP) {
2275         errstr = newvstralloc(errstr, "received strange packet type ",
2276                               pkt_type2str(pkt->type), ": ", pkt->body, NULL);
2277         *response_error = 1;
2278         return;
2279     }
2280
2281 #if defined(PACKET_DEBUG)
2282     fprintf(stderr, "got response:\n----\n%s\n----\n\n", pkt->body);
2283 #endif
2284
2285     for(i = 0; i < NSTREAMS; i++) {
2286         ports[i] = -1;
2287         amidxtaped_streams[i].fd = NULL;
2288     }
2289
2290     p = pkt->body;
2291     while((tok = strtok(p, " \n")) != NULL) {
2292         p = NULL;
2293
2294         /*
2295          * Error response packets have "ERROR" followed by the error message
2296          * followed by a newline.
2297          */
2298         if (strcmp(tok, "ERROR") == 0) {
2299             tok = strtok(NULL, "\n");
2300             if (tok == NULL)
2301                 tok = "[bogus error packet]";
2302             errstr = newstralloc(errstr, tok);
2303             *response_error = 2;
2304             return;
2305         }
2306
2307
2308         /*
2309          * Regular packets have CONNECT followed by three streams
2310          */
2311         if (strcmp(tok, "CONNECT") == 0) {
2312
2313             /*
2314              * Parse the three stream specifiers out of the packet.
2315              */
2316             for (i = 0; i < NSTREAMS; i++) {
2317                 tok = strtok(NULL, " ");
2318                 if (tok == NULL || strcmp(tok, amidxtaped_streams[i].name) != 0) {
2319                     extra = vstralloc("CONNECT token is \"",
2320                                       tok ? tok : "(null)",
2321                                       "\": expected \"",
2322                                       amidxtaped_streams[i].name,
2323                                       "\"",
2324                                       NULL);
2325                     goto parse_error;
2326                 }
2327                 tok = strtok(NULL, " \n");
2328                 if (tok == NULL || sscanf(tok, "%d", &ports[i]) != 1) {
2329                     extra = vstralloc("CONNECT ",
2330                                       amidxtaped_streams[i].name,
2331                                       " token is \"",
2332                                       tok ? tok : "(null)",
2333                                       "\": expected a port number",
2334                                       NULL);
2335                     goto parse_error;
2336                 }
2337             }
2338             continue;
2339         }
2340
2341         /*
2342          * OPTIONS [options string] '\n'
2343          */
2344         if (strcmp(tok, "OPTIONS") == 0) {
2345             tok = strtok(NULL, "\n");
2346             if (tok == NULL) {
2347                 extra = stralloc("OPTIONS token is missing");
2348                 goto parse_error;
2349             }
2350 /*
2351             while((p = strchr(tok, ';')) != NULL) {
2352                 *p++ = '\0';
2353 #define sc "features="
2354                 if(strncmp(tok, sc, sizeof(sc)-1) == 0) {
2355                     tok += sizeof(sc) - 1;
2356 #undef sc
2357                     am_release_feature_set(their_features);
2358                     if((their_features = am_string_to_feature(tok)) == NULL) {
2359                         errstr = newvstralloc(errstr,
2360                                               "OPTIONS: bad features value: ",
2361                                               tok,
2362                                               NULL);
2363                         goto parse_error;
2364                     }
2365                 }
2366                 tok = p;
2367             }
2368 */
2369             continue;
2370         }
2371 /*
2372         extra = vstralloc("next token is \"",
2373                           tok ? tok : "(null)",
2374                           "\": expected \"CONNECT\", \"ERROR\" or \"OPTIONS\"",
2375                           NULL);
2376         goto parse_error;
2377 */
2378     }
2379
2380     /*
2381      * Connect the streams to their remote ports
2382      */
2383     for (i = 0; i < NSTREAMS; i++) {
2384         if (ports[i] == -1)
2385             continue;
2386         amidxtaped_streams[i].fd = security_stream_client(sech, ports[i]);
2387         dbprintf(("amidxtaped_streams[%d].fd = %p\n",i, amidxtaped_streams[i].fd));
2388         if (amidxtaped_streams[i].fd == NULL) {
2389             errstr = newvstralloc(errstr,
2390                         "[could not connect ", amidxtaped_streams[i].name, " stream: ",
2391                         security_geterror(sech), "]", NULL);
2392             goto connect_error;
2393         }
2394     }
2395     /*
2396      * Authenticate the streams
2397      */
2398     for (i = 0; i < NSTREAMS; i++) {
2399         if (amidxtaped_streams[i].fd == NULL)
2400             continue;
2401         if (security_stream_auth(amidxtaped_streams[i].fd) < 0) {
2402             errstr = newvstralloc(errstr,
2403                 "[could not authenticate ", amidxtaped_streams[i].name, " stream: ",
2404                 security_stream_geterror(amidxtaped_streams[i].fd), "]", NULL);
2405             goto connect_error;
2406         }
2407     }
2408
2409     /*
2410      * The CTLFD and DATAFD streams are mandatory.  If we didn't get
2411      * them, complain.
2412      */
2413     if (amidxtaped_streams[CTLFD].fd == NULL) {
2414         errstr = newstralloc(errstr, "[couldn't open CTL streams]");
2415         goto connect_error;
2416     }
2417     if (amidxtaped_streams[DATAFD].fd == NULL) {
2418         errstr = newstralloc(errstr, "[couldn't open DATA streams]");
2419         goto connect_error;
2420     }
2421
2422     /* everything worked */
2423     *response_error = 0;
2424     return;
2425
2426 parse_error:
2427     errstr = newvstralloc(errstr,
2428                           "[parse of reply message failed: ",
2429                           extra ? extra : "(no additional information)",
2430                           "]",
2431                           NULL);
2432     amfree(extra);
2433     *response_error = 2;
2434     return;
2435
2436 connect_error:
2437     stop_amidxtaped();
2438     *response_error = 1;
2439 }
2440
2441 /*
2442  * This is called when everything needs to shut down so event_loop()
2443  * will exit.
2444  */
2445 static void
2446 stop_amidxtaped(void)
2447 {
2448     int i;
2449
2450     for (i = 0; i < NSTREAMS; i++) {
2451         if (amidxtaped_streams[i].fd != NULL) {
2452             security_stream_close(amidxtaped_streams[i].fd);
2453             amidxtaped_streams[i].fd = NULL;
2454         }
2455     }
2456 }
2457
2458 static char* ctl_buffer = NULL;
2459 /* gets a "line" from server and put in server_line */
2460 /* server_line is terminated with \0, \r\n is striped */
2461 /* returns -1 if error */
2462
2463 int
2464 get_amidxtaped_line(void)
2465 {
2466     ssize_t size;
2467     char *newbuf, *s;
2468     void *buf;
2469
2470     amfree(amidxtaped_line);
2471     if (!ctl_buffer)
2472         ctl_buffer = stralloc("");
2473
2474     while (!strstr(ctl_buffer,"\r\n")) {
2475         size = security_stream_read_sync(amidxtaped_streams[CTLFD].fd, &buf);
2476         if(size < 0) {
2477             return -1;
2478         }
2479         else if(size == 0) {
2480             return -1;
2481         }
2482         newbuf = alloc(strlen(ctl_buffer)+size+1);
2483         strncpy(newbuf, ctl_buffer, (size_t)(strlen(ctl_buffer) + size + 1));
2484         memcpy(newbuf+strlen(ctl_buffer), buf, (size_t)size);
2485         newbuf[strlen(ctl_buffer)+size] = '\0';
2486         amfree(ctl_buffer);
2487         ctl_buffer = newbuf;
2488     }
2489
2490     s = strstr(ctl_buffer,"\r\n");
2491     *s = '\0';
2492     newbuf = stralloc(s+2);
2493     amidxtaped_line = stralloc(ctl_buffer);
2494     amfree(ctl_buffer);
2495     ctl_buffer = newbuf;
2496     return 0;
2497 }
2498
2499
2500 static void
2501 read_amidxtaped_data(
2502     void *      cookie,
2503     void *      buf,
2504     ssize_t     size)
2505 {
2506     int fd;
2507
2508     assert(cookie != NULL);
2509
2510     fd = *(int *)cookie;
2511     if (size < 0) {
2512         errstr = newstralloc2(errstr, "amidxtaped read: ",
2513                  security_stream_geterror(amidxtaped_streams[DATAFD].fd));
2514         return;
2515     }
2516
2517     /*
2518      * EOF.  Stop and return.
2519      */
2520     if (size == 0) {
2521         security_stream_close(amidxtaped_streams[DATAFD].fd);
2522         amidxtaped_streams[DATAFD].fd = NULL;
2523         /*
2524          * If the mesg fd has also shut down, then we're done.
2525          */
2526         return;
2527     }
2528
2529     assert(buf != NULL);
2530
2531     /*
2532      * We ignore errors while writing to the index file.
2533      */
2534     (void)fullwrite(fd, buf, (size_t)size);
2535     security_stream_read(amidxtaped_streams[DATAFD].fd, read_amidxtaped_data, cookie);
2536 }
2537
2538 char *
2539 amidxtaped_client_get_security_conf(
2540     char *      string,
2541     void *      arg)
2542 {
2543     (void)arg;  /* Quiet unused parameter warning */
2544
2545     if(!string || !*string)
2546         return(NULL);
2547
2548     if(strcmp(string, "auth")==0) {
2549         return(client_getconf_str(CLN_AUTH));
2550     }
2551     if(strcmp(string, "ssh_keys")==0) {
2552         return(client_getconf_str(CLN_SSH_KEYS));
2553     }
2554     return(NULL);
2555 }