Imported Upstream version 2.5.2p1
[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 "conffile.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     SELECT_ARG_TYPE 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 *cmd = NULL;
740     char *err = NULL;
741     int i;
742     ssize_t j;
743     char *dir, *dir_undo, dir_undo_ch = '\0';
744     char *ditem_path = NULL;
745     char *qditem_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     } else {
767         /* remove "/" at end of path */
768         j = (ssize_t)(strlen(regex) - 1);
769         while(j >= 0 && regex[j] == '/')
770             regex[j--] = '\0';
771     }
772
773     /* convert path (assumed in cwd) to one on disk */
774     if (strcmp(disk_path, "/") == 0) {
775         if (*regex == '/') {
776             /* No mods needed if already starts with '/' */
777             path_on_disk = stralloc(regex);
778         } else {
779             /* Prepend '/' */
780             path_on_disk = stralloc2("/", regex);
781         }
782     } else {
783         char *clean_disk_path = clean_regex(disk_path);
784         path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
785         amfree(clean_disk_path);
786     }
787
788     dbprintf(("add_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n",
789               regex, path_on_disk));
790
791     found_one = 0;
792     dir_entries = 0;
793     for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
794     {
795         dir_entries++;
796         quoted = quote_string(ditem->path);
797         dbprintf(("add_file: Pondering ditem->path=%s\n", quoted));
798         amfree(quoted);
799         if (match(path_on_disk, ditem->path))
800         {
801             found_one = 1;
802             j = (ssize_t)strlen(ditem->path);
803             if((j > 0 && ditem->path[j-1] == '/')
804                || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
805             {   /* It is a directory */
806                 ditem_path = newstralloc(ditem_path, ditem->path);
807                 clean_pathname(ditem_path);
808
809                 qditem_path = quote_string(ditem_path);
810                 cmd = stralloc2("ORLD ", qditem_path);
811                 amfree(qditem_path);
812                 if(send_command(cmd) == -1) {
813                     amfree(cmd);
814                     amfree(ditem_path);
815                     amfree(path_on_disk);
816                     exit(1);
817                 }
818                 amfree(cmd);
819                 cmd = NULL;
820                 /* skip preamble */
821                 if ((i = get_reply_line()) == -1) {
822                     amfree(ditem_path);
823                     amfree(path_on_disk);
824                     exit(1);
825                 }
826                 if(i==0) {              /* assume something wrong */
827                     amfree(ditem_path);
828                     amfree(path_on_disk);
829                     l = reply_line();
830                     printf("%s\n", l);
831                     return;
832                 }
833                 dir_undo = NULL;
834                 added=0;
835                 lditem.path = newstralloc(lditem.path, ditem->path);
836                 /* skip the last line -- duplicate of the preamble */
837
838                 while ((i = get_reply_line()) != 0) {
839                     if (i == -1) {
840                         amfree(ditem_path);
841                         amfree(path_on_disk);
842                         exit(1);
843                     }
844                     if(err) {
845                         if(cmd == NULL) {
846                             if(dir_undo) *dir_undo = dir_undo_ch;
847                             dir_undo = NULL;
848                             cmd = stralloc(l);  /* save for error report */
849                         }
850                         continue;       /* throw the rest of the lines away */
851                     }
852                     l=reply_line();
853                     if (!server_happy()) {
854                         puts(l);
855                         continue;
856                     }
857
858                     s = l;
859                     if(strncmp_const_skip(l, "201-", s, ch) != 0) {
860                         err = "bad reply: not 201-";
861                         continue;
862                     }
863                     ch = *s++;
864
865                     skip_whitespace(s, ch);
866                     if(ch == '\0') {
867                         err = "bad reply: missing date field";
868                         continue;
869                     }
870                     fp = s-1;
871                     skip_non_whitespace(s, ch);
872                     s[-1] = '\0';
873                     lditem.date = newstralloc(lditem.date, fp);
874                     s[-1] = (char)ch;
875
876                     skip_whitespace(s, ch);
877                     if(ch == '\0' || sscanf(s - 1, "%d", &lditem.level) != 1) {
878                         err = "bad reply: cannot parse level field";
879                         continue;
880                     }
881                     skip_integer(s, ch);
882
883                     skip_whitespace(s, ch);
884                     if(ch == '\0') {
885                         err = "bad reply: missing tape field";
886                         continue;
887                     }
888                     fp = s-1;
889                     skip_non_whitespace(s, ch);
890                     s[-1] = '\0';
891                     lditem.tape = newstralloc(lditem.tape, fp);
892                     s[-1] = (char)ch;
893
894                     if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
895                         OFF_T_FMT_TYPE fileno_ = (OFF_T_FMT_TYPE)0;
896                         skip_whitespace(s, ch);
897                         if(ch == '\0' ||
898                            sscanf(s - 1, OFF_T_FMT, &fileno_) != 1) {
899                             err = "bad reply: cannot parse fileno field";
900                             continue;
901                         }
902                         lditem.fileno = (off_t)fileno_;
903                         skip_integer(s, ch);
904                     }
905
906                     skip_whitespace(s, ch);
907                     if(ch == '\0') {
908                         err = "bad reply: missing directory field";
909                         continue;
910                     }
911                     dir = s - 1;
912                     skip_quoted_string(s, ch);
913                     dir_undo = s - 1;
914                     dir_undo_ch = *dir_undo;
915                     *dir_undo = '\0';
916
917                     switch(add_extract_item(&lditem)) {
918                     case -1:
919                         printf("System error\n");
920                         dbprintf(("add_file: (Failed) System error\n"));
921                         break;
922
923                     case  0:
924                         quoted = quote_string(lditem.path);
925                         printf("Added dir %s at date %s\n",
926                                quoted, lditem.date);
927                         dbprintf(("add_file: (Successful) Added dir %s at date %s\n",
928                                   quoted, lditem.date));
929                         amfree(quoted);
930                         added=1;
931                         break;
932
933                     case  1:
934                         break;
935                     }
936                 }
937                 if(!server_happy()) {
938                     puts(reply_line());
939                 } else if(err) {
940                     if (*err)
941                         puts(err);
942                     if (cmd)
943                         puts(cmd);
944                 } else if(added == 0) {
945                     quoted = quote_string(ditem_path);
946                     printf("dir %s already added\n", quoted);
947                     dbprintf(("add_file: dir %s already added\n", quoted));
948                     amfree(quoted);
949                 }
950             }
951             else /* It is a file */
952             {
953                 switch(add_extract_item(ditem)) {
954                 case -1:
955                     printf("System error\n");
956                     dbprintf(("add_file: (Failed) System error\n"));
957                     break;
958
959                 case  0:
960                     quoted = quote_string(ditem->path);
961                     printf("Added file %s\n", quoted);
962                     dbprintf(("add_file: (Successful) Added %s\n", quoted));
963                     amfree(quoted);
964                     break;
965
966                 case  1:
967                     quoted = quote_string(ditem->path);
968                     printf("File %s already added\n", quoted);
969                     dbprintf(("add_file: file %s already added\n", quoted));
970                     amfree(quoted);
971                 }
972             }
973         }
974     }
975
976     amfree(cmd);
977     amfree(ditem_path);
978     amfree(path_on_disk);
979
980     amfree(lditem.path);
981     amfree(lditem.date);
982     amfree(lditem.tape);
983
984     if(! found_one) {
985         quoted = quote_string(path);
986         printf("File %s doesn't exist in directory\n", quoted);
987         dbprintf(("add_file: (Failed) File %s doesn't exist in directory\n",
988                   quoted));
989         amfree(quoted);
990     }
991 }
992
993
994 void
995 delete_glob(
996     char *      glob)
997 {
998     char *regex;
999     char *regex_path;
1000     char *s;
1001     char *uqglob = unquote_string(glob);
1002  
1003     regex = glob_to_regex(uqglob);
1004     dbprintf(("delete_glob (%s) -> %s\n", uqglob, regex));
1005     if ((s = validate_regexp(regex)) != NULL) {
1006         printf("\"%s\" is not a valid shell wildcard pattern: ", glob);
1007         puts(s);
1008     } else {
1009         /*
1010          * glob_to_regex() anchors the beginning of the pattern with ^,
1011          * but we will be tacking it onto the end of the current directory
1012          * in add_file, so strip that off.  Also, it anchors the end with
1013          * $, but we need to match an optional trailing /, so tack that on
1014          * the end.
1015          */
1016         regex_path = stralloc(regex + 1);
1017         regex_path[strlen(regex_path) - 1] = '\0';
1018         strappend(regex_path, "[/]*$");
1019         delete_file(uqglob, regex_path);
1020         amfree(regex_path);
1021     }
1022     amfree(regex);
1023     amfree(uqglob);
1024 }
1025
1026 void
1027 delete_regex(
1028     char *      regex)
1029 {
1030     char *s;
1031     char *uqregex = unquote_string(regex);
1032
1033     if ((s = validate_regexp(regex)) != NULL) {
1034         printf("\"%s\" is not a valid regular expression: ", regex);
1035         puts(s);
1036     } else {
1037         delete_file(uqregex, uqregex);
1038     }
1039     amfree(uqregex);
1040 }
1041
1042 void
1043 delete_file(
1044     char *      path,
1045     char *      regex)
1046 {
1047     DIR_ITEM *ditem, lditem;
1048     char *path_on_disk = NULL;
1049     char *cmd = NULL;
1050     char *err = NULL;
1051     int i;
1052     ssize_t j;
1053     char *date;
1054     char *tape, *tape_undo, tape_undo_ch = '\0';
1055     char *dir_undo, dir_undo_ch = '\0';
1056     int  level = 0;
1057     off_t fileno;
1058     char *ditem_path = NULL;
1059     char *qditem_path;
1060     char *l = NULL;
1061     int  deleted;
1062     char *s;
1063     int ch;
1064     int found_one;
1065     char *quoted;
1066
1067     if (disk_path == NULL) {
1068         printf("Must select directory before deleting files\n");
1069         return;
1070     }
1071     memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
1072
1073     dbprintf(("delete_file: Looking for \"%s\"\n", path));
1074
1075     if (strcmp(regex, "[^/]*[/]*$") == 0) {
1076         /* Looking for * find everything but single . */
1077         regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
1078     } else {
1079         /* remove "/" at end of path */
1080         j = (ssize_t)(strlen(regex) - 1);
1081         while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
1082     }
1083
1084     /* convert path (assumed in cwd) to one on disk */
1085     if (strcmp(disk_path, "/") == 0) {
1086         if (*regex == '/') {
1087             if (strcmp(regex, "/[/]*$") == 0) {
1088                 /* We want "/" to match the directory itself: "/." */
1089                 path_on_disk = stralloc("/\\.[/]*$");
1090             } else {
1091                 /* No mods needed if already starts with '/' */
1092                 path_on_disk = stralloc(regex);
1093             }
1094         } else {
1095             /* Prepend '/' */
1096             path_on_disk = stralloc2("/", regex);
1097         }
1098     } else {
1099         char *clean_disk_path = clean_regex(disk_path);
1100         path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
1101         amfree(clean_disk_path);
1102     }
1103
1104     dbprintf(("delete_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n",
1105               regex, path_on_disk));
1106     found_one = 0;
1107     for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
1108     {
1109         quoted = quote_string(ditem->path);
1110         dbprintf(("delete_file: Pondering ditem->path=%s\n", quoted));
1111         amfree(quoted);
1112         if (match(path_on_disk, ditem->path))
1113         {
1114             found_one = 1;
1115             j = (ssize_t)strlen(ditem->path);
1116             if((j > 0 && ditem->path[j-1] == '/')
1117                || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
1118             {   /* It is a directory */
1119                 ditem_path = newstralloc(ditem_path, ditem->path);
1120                 clean_pathname(ditem_path);
1121
1122                 qditem_path = quote_string(ditem_path);
1123                 cmd = stralloc2("ORLD ", qditem_path);
1124                 amfree(qditem_path);
1125                 if(send_command(cmd) == -1) {
1126                     amfree(cmd);
1127                     amfree(ditem_path);
1128                     amfree(path_on_disk);
1129                     exit(1);
1130                 }
1131                 amfree(cmd);
1132                 /* skip preamble */
1133                 if ((i = get_reply_line()) == -1) {
1134                     amfree(ditem_path);
1135                     amfree(path_on_disk);
1136                     exit(1);
1137                 }
1138                 if(i==0)                /* assume something wrong */
1139                 {
1140                     amfree(ditem_path);
1141                     amfree(path_on_disk);
1142                     l = reply_line();
1143                     printf("%s\n", l);
1144                     return;
1145                 }
1146                 deleted=0;
1147                 lditem.path = newstralloc(lditem.path, ditem->path);
1148                 amfree(cmd);
1149                 tape_undo = dir_undo = NULL;
1150                 /* skip the last line -- duplicate of the preamble */
1151                 while ((i = get_reply_line()) != 0)
1152                 {
1153                     if (i == -1) {
1154                         amfree(ditem_path);
1155                         amfree(path_on_disk);
1156                         exit(1);
1157                     }
1158                     if(err) {
1159                         if(cmd == NULL) {
1160                             if(tape_undo) *tape_undo = tape_undo_ch;
1161                             if(dir_undo) *dir_undo = dir_undo_ch;
1162                             tape_undo = dir_undo = NULL;
1163                             cmd = stralloc(l);  /* save for the error report */
1164                         }
1165                         continue;       /* throw the rest of the lines away */
1166                     }
1167                     l=reply_line();
1168                     if (!server_happy()) {
1169                         puts(l);
1170                         continue;
1171                     }
1172
1173                     s = l;
1174                     if(strncmp_const_skip(l, "201-", s, ch) != 0) {
1175                         err = "bad reply: not 201-";
1176                         continue;
1177                     }
1178                     ch = *s++;
1179
1180                     skip_whitespace(s, ch);
1181                     if(ch == '\0') {
1182                         err = "bad reply: missing date field";
1183                         continue;
1184                     }
1185                     date = s - 1;
1186                     skip_non_whitespace(s, ch);
1187                     *(s - 1) = '\0';
1188
1189                     skip_whitespace(s, ch);
1190                     if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1191                         err = "bad reply: cannot parse level field";
1192                         continue;
1193                     }
1194                     skip_integer(s, ch);
1195
1196                     skip_whitespace(s, ch);
1197                     if(ch == '\0') {
1198                         err = "bad reply: missing tape field";
1199                         continue;
1200                     }
1201                     tape = s - 1;
1202                     skip_non_whitespace(s, ch);
1203                     tape_undo = s - 1;
1204                     tape_undo_ch = *tape_undo;
1205                     *tape_undo = '\0';
1206
1207                     if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
1208                         OFF_T_FMT_TYPE fileno_ = (OFF_T_FMT_TYPE)0;
1209                         skip_whitespace(s, ch);
1210                         if(ch == '\0' ||
1211                            sscanf(s - 1, OFF_T_FMT, &fileno_) != 1) {
1212                             err = "bad reply: cannot parse fileno field";
1213                             continue;
1214                         }
1215                         fileno = (off_t)fileno_;
1216                         skip_integer(s, ch);
1217                     }
1218
1219                     skip_whitespace(s, ch);
1220                     if(ch == '\0') {
1221                         err = "bad reply: missing directory field";
1222                         continue;
1223                     }
1224                     skip_non_whitespace(s, ch);
1225                     dir_undo = s - 1;
1226                     dir_undo_ch = *dir_undo;
1227                     *dir_undo = '\0';
1228
1229                     lditem.date = newstralloc(lditem.date, date);
1230                     lditem.level=level;
1231                     lditem.tape = newstralloc(lditem.tape, tape);
1232                     switch(delete_extract_item(&lditem)) {
1233                     case -1:
1234                         printf("System error\n");
1235                         dbprintf(("delete_file: (Failed) System error\n"));
1236                         break;
1237                     case  0:
1238                         printf("Deleted dir %s at date %s\n", ditem_path, date);
1239                         dbprintf(("delete_file: (Successful) Deleted dir %s at date %s\n",
1240                                   ditem_path, date));
1241                         deleted=1;
1242                         break;
1243                     case  1:
1244                         break;
1245                     }
1246                 }
1247                 if(!server_happy()) {
1248                     puts(reply_line());
1249                 } else if(err) {
1250                     if (*err)
1251                         puts(err);
1252                     if (cmd)
1253                         puts(cmd);
1254                 } else if(deleted == 0) {
1255                     printf("Warning - dir '%s' not on tape list\n",
1256                            ditem_path);
1257                     dbprintf(("delete_file: dir '%s' not on tape list\n",
1258                               ditem_path));
1259                 }
1260             }
1261             else
1262             {
1263                 switch(delete_extract_item(ditem)) {
1264                 case -1:
1265                     printf("System error\n");
1266                     dbprintf(("delete_file: (Failed) System error\n"));
1267                     break;
1268                 case  0:
1269                     printf("Deleted %s\n", ditem->path);
1270                     dbprintf(("delete_file: (Successful) Deleted %s\n",
1271                               ditem->path));
1272                     break;
1273                 case  1:
1274                     printf("Warning - file '%s' not on tape list\n",
1275                            ditem->path);
1276                     dbprintf(("delete_file: file '%s' not on tape list\n",
1277                               ditem->path));
1278                     break;
1279                 }
1280             }
1281         }
1282     }
1283     amfree(cmd);
1284     amfree(ditem_path);
1285     amfree(path_on_disk);
1286
1287     if(! found_one) {
1288         printf("File %s doesn't exist in directory\n", path);
1289         dbprintf(("delete_file: (Failed) File %s doesn't exist in directory\n",
1290                   path));
1291     }
1292 }
1293
1294
1295 /* print extract list into file. If NULL ptr passed print to screen */
1296 void
1297 display_extract_list(
1298     char *      file)
1299 {
1300     EXTRACT_LIST *this;
1301     EXTRACT_LIST_ITEM *that;
1302     FILE *fp;
1303     char *pager;
1304     char *pager_command;
1305     char *uqfile;
1306
1307     if (file == NULL)
1308     {
1309         if ((pager = getenv("PAGER")) == NULL)
1310         {
1311             pager = "more";
1312         }
1313         /*
1314          * Set up the pager command so if the pager is terminated, we do
1315          * not get a SIGPIPE back.
1316          */
1317         pager_command = stralloc2(pager, " ; /bin/cat > /dev/null");
1318         if ((fp = popen(pager_command, "w")) == NULL)
1319         {
1320             printf("Warning - can't pipe through %s\n", pager);
1321             fp = stdout;
1322         }
1323         amfree(pager_command);
1324     }
1325     else
1326     {
1327         uqfile = unquote_string(file);
1328         if ((fp = fopen(uqfile, "w")) == NULL)
1329         {
1330             printf("Can't open file %s to print extract list into\n", file);
1331             amfree(uqfile);
1332             return;
1333         }
1334         amfree(uqfile);
1335     }
1336
1337     for (this = extract_list; this != NULL; this = this->next)
1338     {
1339         fprintf(fp, "TAPE %s LEVEL %d DATE %s\n",
1340                 this->tape, this->level, this->date);
1341         for (that = this->files; that != NULL; that = that->next)
1342             fprintf(fp, "\t%s\n", that->path);
1343     }
1344
1345     if (file == NULL) {
1346         apclose(fp);
1347     } else {
1348         printf("Extract list written to file %s\n", file);
1349         afclose(fp);
1350     }
1351 }
1352
1353
1354 static int
1355 is_empty_dir(
1356     char *fname)
1357 {
1358     DIR *dir;
1359     struct dirent *entry;
1360     int gotentry;
1361
1362     if((dir = opendir(fname)) == NULL)
1363         return 1;
1364
1365     gotentry = 0;
1366     while(!gotentry && (entry = readdir(dir)) != NULL) {
1367         gotentry = !is_dot_or_dotdot(entry->d_name);
1368     }
1369
1370     closedir(dir);
1371     return !gotentry;
1372
1373 }
1374
1375 /* returns 0 if extract list empty and 1 if it isn't */
1376 int
1377 is_extract_list_nonempty(void)
1378 {
1379     return (extract_list != NULL);
1380 }
1381
1382
1383 /* prints continue prompt and waits for response,
1384    returns 0 if don't, non-0 if do */
1385 static int
1386 okay_to_continue(
1387     int allow_tape,
1388     int allow_skip,
1389     int allow_retry)
1390 {
1391     int ch;
1392     int ret = -1;
1393     char *line = NULL;
1394     char *s;
1395     char *prompt;
1396     int get_tape;
1397
1398     get_tape = 0;
1399     while (ret < 0) {
1400         if (get_tape) {
1401             prompt = "New tape device [?]: ";
1402         } else if (allow_tape && allow_skip) {
1403             prompt = "Continue [?/Y/n/s/t]? ";
1404         } else if (allow_tape && !allow_skip) {
1405             prompt = "Continue [?/Y/n/t]? ";
1406         } else if (allow_retry) {
1407             prompt = "Continue [?/Y/n/r]? ";
1408         } else {
1409             prompt = "Continue [?/Y/n]? ";
1410         }
1411         fputs(prompt, stdout);
1412         fflush(stdout); fflush(stderr);
1413         amfree(line);
1414         if ((line = agets(stdin)) == NULL) {
1415             putchar('\n');
1416             clearerr(stdin);
1417             if (get_tape) {
1418                 get_tape = 0;
1419                 continue;
1420             }
1421             ret = 0;
1422             break;
1423         }
1424         s = line;
1425         while ((ch = *s++) != '\0' && isspace(ch)) {
1426             (void)ch;   /* Quiet empty loop compiler warning */
1427         }
1428         if (ch == '?') {
1429             if (get_tape) {
1430                 printf("Enter a new device ([host:]device) or \"default\"\n");
1431             } else {
1432                 printf("Enter \"y\"es to continue, \"n\"o to stop");
1433                 if(allow_skip) {
1434                     printf(", \"s\"kip this tape");
1435                 }
1436                 if(allow_retry) {
1437                     printf(" or \"r\"etry this tape");
1438                 }
1439                 if (allow_tape) {
1440                     printf(" or \"t\"ape to change tape drives");
1441                 }
1442                 putchar('\n');
1443             }
1444         } else if (get_tape) {
1445             set_tape(s - 1);
1446             get_tape = 0;
1447         } else if (ch == '\0' || ch == 'Y' || ch == 'y') {
1448             ret = 1;
1449         } else if (allow_tape && (ch == 'T' || ch == 't')) {
1450             get_tape = 1;
1451         } else if (ch == 'N' || ch == 'n') {
1452             ret = 0;
1453         } else if (allow_retry && (ch == 'R' || ch == 'r')) {
1454             ret = RETRY_TAPE;
1455         } else if (allow_skip && (ch == 'S' || ch == 's')) {
1456             ret = SKIP_TAPE;
1457         }
1458     }
1459     /*@ignore@*/
1460     amfree(line);
1461     /*@end@*/
1462     return ret;
1463 }
1464
1465 static void
1466 send_to_tape_server(
1467     security_stream_t * stream,
1468     char *              cmd)
1469 {
1470     char *msg = stralloc2(cmd, "\r\n");
1471
1472     if (security_stream_write(stream, msg, strlen(msg)) < 0)
1473     {
1474         error("Error writing to tape server");
1475         exit(101);
1476         /*NOTREACHED*/
1477     }
1478     amfree(msg);
1479 }
1480
1481
1482 /* start up connection to tape server and set commands to initiate
1483    transfer of dump image.
1484    Return tape server socket on success, -1 on error. */
1485 static int
1486 extract_files_setup(
1487     char *      label,
1488     off_t       fsf)
1489 {
1490     char *disk_regex = NULL;
1491     char *host_regex = NULL;
1492     char *clean_datestamp, *ch, *ch1;
1493     char *tt = NULL;
1494     char *req;
1495     int response_error;
1496
1497     amidxtaped_secdrv = security_getdriver(authopt);
1498     if (amidxtaped_secdrv == NULL) {
1499         error("no '%s' security driver available for host '%s'",
1500               authopt, tape_server_name);
1501     }
1502
1503     /* We assume that amidxtaped support fe_amidxtaped_options_features */
1504     /*                               and fe_amidxtaped_options_auth     */
1505     /* We should send a noop to really know                             */
1506     req = vstralloc("SERVICE amidxtaped\n",
1507                     "OPTIONS ", "features=", our_features_string, ";",
1508                                 "auth=", authopt, ";",
1509                     "\n", NULL);
1510     protocol_sendreq(tape_server_name, amidxtaped_secdrv,
1511                      generic_client_get_security_conf, req, STARTUP_TIMEOUT,
1512                      amidxtaped_response, &response_error);
1513     amfree(req);
1514     protocol_run();
1515     if(response_error != 0) {
1516         return -1;
1517     }
1518
1519     disk_regex = alloc(strlen(disk_name) * 2 + 3);
1520
1521     ch = disk_name;
1522     ch1 = disk_regex;
1523
1524     /* we want to force amrestore to only match disk_name exactly */
1525     *(ch1++) = '^';
1526
1527     /* We need to escape some characters first... NT compatibilty crap */
1528     for (; *ch != 0; ch++, ch1++) {
1529         switch (*ch) {     /* done this way in case there are more */
1530         case '$':
1531             *(ch1++) = '\\';
1532             /* no break; we do want to fall through... */
1533         default:
1534             *ch1 = *ch;
1535         }
1536     }
1537
1538     /* we want to force amrestore to only match disk_name exactly */
1539     *(ch1++) = '$';
1540
1541     *ch1 = '\0';
1542
1543     host_regex = alloc(strlen(dump_hostname) * 2 + 3);
1544
1545     ch = dump_hostname;
1546     ch1 = host_regex;
1547
1548     /* we want to force amrestore to only match dump_hostname exactly */
1549     *(ch1++) = '^';
1550
1551     /* We need to escape some characters first... NT compatibilty crap */
1552     for (; *ch != 0; ch++, ch1++) {
1553         switch (*ch) {     /* done this way in case there are more */
1554         case '$':
1555             *(ch1++) = '\\';
1556             /* no break; we do want to fall through... */
1557         default:
1558             *ch1 = *ch;
1559         }
1560     }
1561
1562     /* we want to force amrestore to only match dump_hostname exactly */
1563     *(ch1++) = '$';
1564
1565     *ch1 = '\0';
1566
1567     clean_datestamp = stralloc(dump_datestamp);
1568     for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) {
1569         if(*ch1 != '-') {
1570             *ch = *ch1;
1571             ch++;
1572         }
1573     }
1574     *ch = '\0';
1575     /* push our feature list off to the tape server */
1576     /* XXX assumes that index server and tape server are equivalent, ew */
1577
1578     if(am_has_feature(indexsrv_features, fe_amidxtaped_exchange_features)){
1579         tt = newstralloc2(tt, "FEATURES=", our_features_string);
1580         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1581         get_amidxtaped_line();
1582         if(strncmp_const(amidxtaped_line,"FEATURES=") == 0) {
1583             tapesrv_features = am_string_to_feature(amidxtaped_line+9);
1584         } else {
1585             fprintf(stderr, "amrecover - expecting FEATURES line from amidxtaped\n");
1586             stop_amidxtaped();
1587             amfree(disk_regex);
1588             amfree(host_regex);
1589             amfree(clean_datestamp);
1590             return -1;
1591         }
1592         am_release_feature_set(tapesrv_features);
1593     }
1594
1595
1596     if(am_has_feature(indexsrv_features, fe_amidxtaped_header) &&
1597        am_has_feature(indexsrv_features, fe_amidxtaped_device) &&
1598        am_has_feature(indexsrv_features, fe_amidxtaped_host) &&
1599        am_has_feature(indexsrv_features, fe_amidxtaped_disk) &&
1600        am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) {
1601
1602         if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) {
1603             tt = newstralloc2(tt, "CONFIG=", config);
1604             send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1605         }
1606         if(am_has_feature(indexsrv_features, fe_amidxtaped_label) &&
1607            label && label[0] != '/') {
1608             tt = newstralloc2(tt,"LABEL=",label);
1609             send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1610         }
1611         if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) {
1612             char v_fsf[100];
1613             snprintf(v_fsf, 99, OFF_T_FMT, (OFF_T_FMT_TYPE)fsf);
1614             tt = newstralloc2(tt, "FSF=",v_fsf);
1615             send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1616         }
1617         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "HEADER");
1618         tt = newstralloc2(tt, "DEVICE=", dump_device_name);
1619         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1620         tt = newstralloc2(tt, "HOST=", host_regex);
1621         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1622         tt = newstralloc2(tt, "DISK=", disk_regex);
1623         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1624         tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp);
1625         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1626         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "END");
1627         amfree(tt);
1628     }
1629     else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) {
1630         /* send to the tape server what tape file we want */
1631         /* 6 args:
1632          *   "-h"
1633          *   "-p"
1634          *   "tape device"
1635          *   "hostname"
1636          *   "diskname"
1637          *   "datestamp"
1638          */
1639         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "6");
1640         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "-h");
1641         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "-p");
1642         send_to_tape_server(amidxtaped_streams[CTLFD].fd, dump_device_name);
1643         send_to_tape_server(amidxtaped_streams[CTLFD].fd, host_regex);
1644         send_to_tape_server(amidxtaped_streams[CTLFD].fd, disk_regex);
1645         send_to_tape_server(amidxtaped_streams[CTLFD].fd, clean_datestamp);
1646
1647         dbprintf(("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n",
1648                   dump_device_name, host_regex, disk_regex, clean_datestamp));
1649     }
1650
1651     amfree(disk_regex);
1652     amfree(host_regex);
1653     amfree(clean_datestamp);
1654
1655     return 0;
1656 }
1657
1658
1659 /*
1660  * Reads the first block of a tape file.
1661  */
1662
1663 void
1664 read_file_header(
1665     char *      buffer,
1666     dumpfile_t *file,
1667     size_t      buflen,
1668     int         tapedev)
1669 {
1670     ssize_t bytes_read;
1671     bytes_read = read_buffer(tapedev, buffer, buflen, READ_TIMEOUT);
1672     if(bytes_read < 0) {
1673         error("error reading header (%s), check amidxtaped.*.debug on server",
1674               strerror(errno));
1675         /*NOTREACHED*/
1676     }
1677
1678     if((size_t)bytes_read < buflen) {
1679         fprintf(stderr, "%s: short block %d byte%s\n",
1680                 get_pname(), (int)bytes_read, (bytes_read == 1) ? "" : "s");
1681         print_header(stdout, file);
1682         error("Can't read file header");
1683         /*NOTREACHED*/
1684     }
1685
1686     /* bytes_read == buflen */
1687     parse_file_header(buffer, file, (size_t)bytes_read);
1688 }
1689
1690 enum dumptypes {
1691         IS_UNKNOWN,
1692         IS_DUMP,
1693         IS_GNUTAR,
1694         IS_TAR,
1695         IS_SAMBA,
1696         IS_SAMBA_TAR,
1697         IS_BACKUP_API
1698 };
1699
1700 static void
1701 extract_files_child(
1702     int                 in_fd,
1703     EXTRACT_LIST *      elist)
1704 {
1705     int save_errno;
1706     int extra_params = 0;
1707     int i,j=0;
1708     char **restore_args = NULL;
1709     int files_off_tape;
1710     EXTRACT_LIST_ITEM *fn;
1711     enum dumptypes dumptype = IS_UNKNOWN;
1712     char buffer[DISK_BLOCK_BYTES];
1713     dumpfile_t file;
1714     size_t len_program;
1715     char *cmd = NULL;
1716     int passwd_field = -1;
1717 #ifdef SAMBA_CLIENT
1718     char *domain = NULL, *smbpass = NULL;
1719 #endif
1720
1721     /* code executed by child to do extraction */
1722     /* never returns */
1723
1724     /* make in_fd be our stdin */
1725     if (dup2(in_fd, STDIN_FILENO) == -1)
1726     {
1727         error("dup2 failed in extract_files_child: %s", strerror(errno));
1728         /*NOTREACHED*/
1729     }
1730
1731     /* read the file header */
1732     fh_init(&file);
1733     read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO);
1734
1735     if(file.type != F_DUMPFILE) {
1736         print_header(stdout, &file);
1737         error("bad header");
1738         /*NOTREACHED*/
1739     }
1740
1741     if (file.program != NULL) {
1742         if (strcmp(file.program, "BACKUP") == 0)
1743             dumptype = IS_BACKUP_API;
1744 #ifdef GNUTAR
1745         if (strcmp(file.program, GNUTAR) == 0)
1746             dumptype = IS_GNUTAR;
1747 #endif
1748
1749         if (dumptype == IS_UNKNOWN) {
1750             len_program = strlen(file.program);
1751             if(len_program >= 3 &&
1752                strcmp(&file.program[len_program-3],"tar") == 0)
1753                 dumptype = IS_TAR;
1754         }
1755
1756 #ifdef SAMBA_CLIENT
1757         if (dumptype == IS_UNKNOWN && strcmp(file.program, SAMBA_CLIENT) ==0) {
1758             if (samba_extract_method == SAMBA_TAR)
1759               dumptype = IS_SAMBA_TAR;
1760             else
1761               dumptype = IS_SAMBA;
1762         }
1763 #endif
1764     }
1765
1766     /* form the arguments to restore */
1767     files_off_tape = length_of_tape_list(elist);
1768     switch (dumptype) {
1769     case IS_SAMBA:
1770 #ifdef SAMBA_CLIENT
1771         extra_params = 10;
1772         break;
1773 #endif
1774     case IS_TAR:
1775     case IS_GNUTAR:
1776         extra_params = 4;
1777         break;
1778     case IS_SAMBA_TAR:
1779         extra_params = 3;
1780         break;
1781     case IS_UNKNOWN:
1782     case IS_DUMP:
1783 #ifdef AIX_BACKUP
1784         extra_params = 2;
1785 #else
1786 #if defined(XFSDUMP)
1787         if (strcmp(file.program, XFSDUMP) == 0) {
1788             extra_params = 4 + files_off_tape;
1789         } else
1790 #endif
1791         {
1792         extra_params = 4;
1793         }
1794 #endif
1795         break;
1796     case IS_BACKUP_API:
1797         extra_params = 5;
1798         break;
1799     }
1800     restore_args = (char **)alloc((size_t)((extra_params + files_off_tape + 1)
1801                                   * sizeof(char *)));
1802     switch(dumptype) {
1803     case IS_SAMBA:
1804 #ifdef SAMBA_CLIENT
1805         restore_args[j++] = stralloc("smbclient");
1806         smbpass = findpass(file.disk, &domain);
1807         if (smbpass) {
1808             restore_args[j++] = stralloc(file.disk);
1809             passwd_field=j;
1810             restore_args[j++] = stralloc("-U");
1811             restore_args[j++] = smbpass;
1812             if (domain) {
1813                 restore_args[j++] = stralloc("-W");
1814                 restore_args[j++] = stralloc(domain);
1815             } else
1816                 extra_params -= 2;
1817         } else
1818             extra_params -= 6;
1819         restore_args[j++] = stralloc("-d0");
1820         restore_args[j++] = stralloc("-Tx");
1821         restore_args[j++] = stralloc("-");      /* data on stdin */
1822         break;
1823 #endif
1824     case IS_TAR:
1825     case IS_GNUTAR:
1826         restore_args[j++] = stralloc("tar");
1827         restore_args[j++] = stralloc("--numeric-owner");
1828         restore_args[j++] = stralloc("-xpGvf");
1829         restore_args[j++] = stralloc("-");      /* data on stdin */
1830         break;
1831     case IS_SAMBA_TAR:
1832         restore_args[j++] = stralloc("tar");
1833         restore_args[j++] = stralloc("-xpvf");
1834         restore_args[j++] = stralloc("-");      /* data on stdin */
1835         break;
1836     case IS_UNKNOWN:
1837     case IS_DUMP:
1838         restore_args[j++] = stralloc("restore");
1839 #ifdef AIX_BACKUP
1840         restore_args[j++] = stralloc("-xB");
1841 #else
1842 #if defined(XFSDUMP)
1843         if (strcmp(file.program, XFSDUMP) == 0) {
1844             restore_args[j++] = stralloc("-v");
1845             restore_args[j++] = stralloc("silent");
1846         } else
1847 #endif
1848 #if defined(VDUMP)
1849         if (strcmp(file.program, VDUMP) == 0) {
1850             restore_args[j++] = stralloc("xf");
1851             restore_args[j++] = stralloc("-");  /* data on stdin */
1852         } else
1853 #endif
1854         {
1855         restore_args[j++] = stralloc("xbf");
1856         restore_args[j++] = stralloc("2");      /* read in units of 1K */
1857         restore_args[j++] = stralloc("-");      /* data on stdin */
1858         }
1859 #endif
1860         break;
1861     case IS_BACKUP_API:
1862         restore_args[j++] = stralloc(file.dumper);
1863         restore_args[j++] = stralloc("restore");
1864         restore_args[j++] = stralloc("--config");
1865         restore_args[j++] = stralloc(config);
1866         restore_args[j++] = stralloc("--disk");
1867         restore_args[j++] = stralloc(file.disk);
1868         break;
1869     }
1870   
1871     for (i = 0, fn = elist->files; i < files_off_tape; i++, fn = fn->next)
1872     {
1873         switch (dumptype) {
1874         case IS_BACKUP_API:
1875         case IS_TAR:
1876         case IS_GNUTAR:
1877         case IS_SAMBA_TAR:
1878         case IS_SAMBA:
1879             if (strcmp(fn->path, "/") == 0)
1880                 restore_args[j++] = stralloc(".");
1881             else
1882                 restore_args[j++] = stralloc2(".", fn->path);
1883             break;
1884         case IS_UNKNOWN:
1885         case IS_DUMP:
1886 #if defined(XFSDUMP)
1887             if (strcmp(file.program, XFSDUMP) == 0) {
1888                 /*
1889                  * xfsrestore needs a -s option before each file to be
1890                  * restored, and also wants them to be relative paths.
1891                  */
1892                 restore_args[j++] = stralloc("-s");
1893                 restore_args[j++] = stralloc(fn->path + 1);
1894             } else
1895 #endif
1896             {
1897             restore_args[j++] = stralloc(fn->path);
1898             }
1899             break;
1900         }
1901     }
1902 #if defined(XFSDUMP)
1903     if (strcmp(file.program, XFSDUMP) == 0) {
1904         restore_args[j++] = stralloc("-");
1905         restore_args[j++] = stralloc(".");
1906     }
1907 #endif
1908     restore_args[j] = NULL;
1909
1910     switch (dumptype) {
1911     case IS_SAMBA:
1912 #ifdef SAMBA_CLIENT
1913         cmd = stralloc(SAMBA_CLIENT);
1914         break;
1915 #else
1916         /* fall through to ... */
1917 #endif
1918     case IS_TAR:
1919     case IS_GNUTAR:
1920     case IS_SAMBA_TAR:
1921 #ifndef GNUTAR
1922         fprintf(stderr, "warning: GNUTAR program not available.\n");
1923         cmd = stralloc("tar");
1924 #else
1925         cmd = stralloc(GNUTAR);
1926 #endif
1927         break;
1928     case IS_UNKNOWN:
1929     case IS_DUMP:
1930         cmd = NULL;
1931 #if defined(DUMP)
1932         if (strcmp(file.program, DUMP) == 0) {
1933             cmd = stralloc(RESTORE);
1934         }
1935 #endif
1936 #if defined(VDUMP)
1937         if (strcmp(file.program, VDUMP) == 0) {
1938             cmd = stralloc(VRESTORE);
1939         }
1940 #endif
1941 #if defined(VXDUMP)
1942         if (strcmp(file.program, VXDUMP) == 0) {
1943             cmd = stralloc(VXRESTORE);
1944         }
1945 #endif
1946 #if defined(XFSDUMP)
1947         if (strcmp(file.program, XFSDUMP) == 0) {
1948             cmd = stralloc(XFSRESTORE);
1949         }
1950 #endif
1951         if (cmd == NULL) {
1952             fprintf(stderr, "warning: restore program for %s not available.\n",
1953                     file.program);
1954             cmd = stralloc("restore");
1955         }
1956         break;
1957     case IS_BACKUP_API:
1958         cmd = vstralloc(DUMPER_DIR, "/", file.dumper, NULL);
1959         break;
1960     }
1961     if (cmd) {
1962         dbprintf(("Exec'ing %s with arguments:\n", cmd));
1963         for (i = 0; i < j; i++) {
1964             if( i == passwd_field)
1965                 dbprintf(("\tXXXXX\n"));
1966             else
1967                 dbprintf(("\t%s\n", restore_args[i]));
1968         }
1969         (void)execv(cmd, restore_args);
1970         /* only get here if exec failed */
1971         save_errno = errno;
1972         for (i = 0; i < j; i++) {
1973             amfree(restore_args[i]);
1974         }
1975         amfree(restore_args);
1976         errno = save_errno;
1977         perror("amrecover couldn't exec");
1978         fprintf(stderr, " problem executing %s\n", cmd);
1979         amfree(cmd);
1980     }
1981     exit(1);
1982     /*NOT REACHED */
1983 }
1984
1985 /*
1986  * Interpose something between the process writing out the dump (writing it to
1987  * some extraction program, really) and the socket from which we're reading, so
1988  * that we can do things like prompt for human interaction for multiple tapes.
1989  */
1990 int
1991 writer_intermediary(
1992     EXTRACT_LIST *      elist)
1993 {
1994     int child_pipe[2];
1995     pid_t pid;
1996     amwait_t extractor_status;
1997
1998     if(pipe(child_pipe) == -1) {
1999         error("extract_list - error setting up pipe to extractor: %s\n",
2000               strerror(errno));
2001         /*NOTREACHED*/
2002     }
2003
2004     /* okay, ready to extract. fork a child to do the actual work */
2005     if ((pid = fork()) == 0) {
2006         /* this is the child process */
2007         /* never gets out of this clause */
2008         aclose(child_pipe[1]);
2009         extract_files_child(child_pipe[0], elist);
2010         /*NOTREACHED*/
2011     }
2012
2013     /* This is the parent */
2014     if (pid == -1) {
2015         printf("writer_intermediary - error forking child");
2016         return -1;
2017     }
2018
2019     aclose(child_pipe[0]);
2020
2021     security_stream_read(amidxtaped_streams[DATAFD].fd,
2022                          read_amidxtaped_data, &(child_pipe[1]));
2023
2024     while(get_amidxtaped_line() >= 0) {
2025         char desired_tape[MAX_TAPE_LABEL_BUF];
2026                 
2027         /* if prompted for a tape, relay said prompt to the user */
2028         if(sscanf(amidxtaped_line, "FEEDME %132s\n", desired_tape) == 1) {
2029             int done;
2030             printf("Load tape %s now\n", desired_tape);
2031             done = okay_to_continue(am_has_feature(indexsrv_features,
2032                                                    fe_amrecover_feedme_tape),
2033                                     0, 0);
2034             if (done == 1) {
2035                 if (am_has_feature(indexsrv_features,
2036                                    fe_amrecover_feedme_tape)) {
2037                     char *reply = stralloc2("TAPE ", tape_device_name);
2038                     send_to_tape_server(amidxtaped_streams[CTLFD].fd, reply);
2039                     amfree(reply);
2040                 } else {
2041                     send_to_tape_server(amidxtaped_streams[CTLFD].fd, "OK");
2042                 }
2043             } else {
2044                 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "ERROR");
2045                 break;
2046             }
2047         } else if(strncmp_const(amidxtaped_line, "MESSAGE ") == 0) {
2048             printf("%s\n",&amidxtaped_line[8]);
2049         } else {
2050             fprintf(stderr, "Strange message from tape server: %s",
2051                     amidxtaped_line);
2052             break;
2053         }
2054     }
2055
2056     /* CTL might be close before DATA */
2057     event_loop(0);
2058     aclose(child_pipe[1]);
2059
2060     waitpid(pid, &extractor_status, 0);
2061     if(WEXITSTATUS(extractor_status) != 0){
2062         int ret = WEXITSTATUS(extractor_status);
2063         if(ret == 255) ret = -1;
2064         printf("Extractor child exited with status %d\n", ret);
2065         return -1;
2066     }
2067     return(0);
2068 }
2069
2070 /* exec restore to do the actual restoration */
2071
2072 /* does the actual extraction of files */
2073 /*
2074  * The original design had the dump image being returned exactly as it
2075  * appears on the tape, and this routine getting from the index server
2076  * whether or not it is compressed, on the assumption that the tape
2077  * server may not know how to uncompress it. But
2078  * - Amrestore can't do that. It returns either compressed or uncompressed
2079  * (always). Amrestore assumes it can uncompress files. It is thus a good
2080  * idea to run the tape server on a machine with gzip.
2081  * - The information about compression in the disklist is really only
2082  * for future dumps. It is possible to change compression on a drive
2083  * so the information in the disklist may not necessarily relate to
2084  * the dump image on the tape.
2085  *   Consequently the design was changed to assuming that amrestore can
2086  * uncompress any dump image and have it return an uncompressed file
2087  * always.
2088  */
2089 void
2090 extract_files(void)
2091 {
2092     EXTRACT_LIST *elist;
2093     char cwd[STR_SIZE];
2094     char *l;
2095     int first;
2096     int otc;
2097     tapelist_t *tlist = NULL, *a_tlist;
2098
2099     if (!is_extract_list_nonempty())
2100     {
2101         printf("Extract list empty - No files to extract!\n");
2102         return;
2103     }
2104
2105     clean_extract_list();
2106
2107     /* get tape device name from index server if none specified */
2108     if (tape_server_name == NULL) {
2109         tape_server_name = newstralloc(tape_server_name, server_name);
2110     }
2111     if (tape_device_name == NULL) {
2112         if (send_command("TAPE") == -1)
2113             exit(1);
2114         if (get_reply_line() == -1)
2115             exit(1);
2116         l = reply_line();
2117         if (!server_happy())
2118         {
2119             printf("%s\n", l);
2120             exit(1);
2121         }
2122         /* skip reply number */
2123         tape_device_name = newstralloc(tape_device_name, l+4);
2124     }
2125
2126     if (strcmp(tape_device_name, "/dev/null") == 0)
2127     {
2128         printf("amrecover: warning: using %s as the tape device will not work\n",
2129                tape_device_name);
2130     }
2131
2132     first=1;
2133     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) {
2134         if(elist->tape[0]!='/') {
2135             if(first) {
2136                 printf("\nExtracting files using tape drive %s on host %s.\n",
2137                         tape_device_name, tape_server_name);
2138                 printf("The following tapes are needed:");
2139                 first=0;
2140             }
2141             else
2142                 printf("                               ");
2143             tlist = unmarshal_tapelist_str(elist->tape); 
2144             for(a_tlist = tlist ; a_tlist != NULL; a_tlist = a_tlist->next)
2145                 printf(" %s", a_tlist->label);
2146             printf("\n");
2147             free_tapelist(tlist);
2148         }
2149     }
2150     first=1;
2151     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) {
2152         if(elist->tape[0]=='/') {
2153             if(first) {
2154                 printf("\nExtracting files from holding disk on host %s.\n",
2155                         tape_server_name);
2156                 printf("The following files are needed:");
2157                 first=0;
2158             }
2159             else
2160                 printf("                               ");
2161             tlist = unmarshal_tapelist_str(elist->tape); 
2162             for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next)
2163                 printf(" %s", a_tlist->label);
2164             printf("\n");
2165             free_tapelist(tlist);
2166         }
2167     }
2168     printf("\n");
2169
2170     if (getcwd(cwd, sizeof(cwd)) == NULL) {
2171         perror("extract_list: Current working directory unavailable");
2172         exit(1);
2173     }
2174
2175     printf("Restoring files into directory %s\n", cwd);
2176     check_file_overwrite(cwd);
2177
2178 #ifdef SAMBA_CLIENT
2179     if (samba_extract_method == SAMBA_SMBCLIENT)
2180       printf("(unless it is a Samba backup, that will go through to the SMB server)\n");
2181 #endif
2182     if (!okay_to_continue(0,0,0))
2183         return;
2184     printf("\n");
2185
2186     if (!do_unlink_list()) {
2187         fprintf(stderr, "Can't recover because I can't cleanup the cwd (%s)\n",
2188                 cwd);
2189         return;
2190     }
2191     free_unlink_list();
2192
2193     while ((elist = first_tape_list()) != NULL)
2194     {
2195         if(elist->tape[0]=='/') {
2196             dump_device_name = newstralloc(dump_device_name, elist->tape);
2197             printf("Extracting from file ");
2198             tlist = unmarshal_tapelist_str(dump_device_name); 
2199             for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next)
2200                 printf(" %s", a_tlist->label);
2201             printf("\n");
2202             free_tapelist(tlist);
2203         }
2204         else {
2205             printf("Extracting files using tape drive %s on host %s.\n",
2206                    tape_device_name, tape_server_name);
2207             tlist = unmarshal_tapelist_str(elist->tape); 
2208             printf("Load tape %s now\n", tlist->label);
2209             free_tapelist(tlist);
2210             otc = okay_to_continue(1,1,0);
2211             if (otc == 0)
2212                 return;
2213             else if (otc == SKIP_TAPE) {
2214                 delete_tape_list(elist); /* skip this tape */
2215                 continue;
2216             }
2217             dump_device_name = newstralloc(dump_device_name, tape_device_name);
2218         }
2219         dump_datestamp = newstralloc(dump_datestamp, elist->date);
2220
2221         /* connect to the tape handler daemon on the tape drive server */
2222         if ((extract_files_setup(elist->tape, elist->fileno)) == -1)
2223         {
2224             fprintf(stderr, "amrecover - can't talk to tape server: %s\n",errstr);
2225             return;
2226         }
2227
2228         /* if the server have fe_amrecover_feedme_tape, it has asked for
2229          * the tape itself, even if the restore didn't succeed, we should
2230          * remove it.
2231          */
2232         if(writer_intermediary(elist) == 0 ||
2233            am_has_feature(indexsrv_features, fe_amrecover_feedme_tape))
2234             delete_tape_list(elist);    /* tape done so delete from list */
2235
2236         stop_amidxtaped();
2237     }
2238 }
2239
2240 static void
2241 amidxtaped_response(
2242     void *              datap,
2243     pkt_t *             pkt,
2244     security_handle_t * sech)
2245 {
2246     int ports[NSTREAMS], *response_error = datap, i;
2247     char *p;
2248     char *tok;
2249     char *extra = NULL;
2250
2251     assert(response_error != NULL);
2252     assert(sech != NULL);
2253     memset(ports, -1, SIZEOF(ports));
2254
2255     if (pkt == NULL) {
2256         errstr = newvstralloc(errstr, "[request failed: ",
2257                              security_geterror(sech), "]", NULL);
2258         *response_error = 1;
2259         return;
2260     }
2261     security_close_connection(sech, dump_hostname);
2262
2263     if (pkt->type == P_NAK) {
2264 #if defined(PACKET_DEBUG)
2265         fprintf(stderr, "got nak response:\n----\n%s\n----\n\n", pkt->body);
2266 #endif
2267
2268         tok = strtok(pkt->body, " ");
2269         if (tok == NULL || strcmp(tok, "ERROR") != 0)
2270             goto bad_nak;
2271
2272         tok = strtok(NULL, "\n");
2273         if (tok != NULL) {
2274             errstr = newvstralloc(errstr, "NAK: ", tok, NULL);
2275             *response_error = 1;
2276         } else {
2277 bad_nak:
2278             errstr = newstralloc(errstr, "request NAK");
2279             *response_error = 2;
2280         }
2281         return;
2282     }
2283
2284     if (pkt->type != P_REP) {
2285         errstr = newvstralloc(errstr, "received strange packet type ",
2286                               pkt_type2str(pkt->type), ": ", pkt->body, NULL);
2287         *response_error = 1;
2288         return;
2289     }
2290
2291 #if defined(PACKET_DEBUG)
2292     fprintf(stderr, "got response:\n----\n%s\n----\n\n", pkt->body);
2293 #endif
2294
2295     for(i = 0; i < NSTREAMS; i++) {
2296         ports[i] = -1;
2297         amidxtaped_streams[i].fd = NULL;
2298     }
2299
2300     p = pkt->body;
2301     while((tok = strtok(p, " \n")) != NULL) {
2302         p = NULL;
2303
2304         /*
2305          * Error response packets have "ERROR" followed by the error message
2306          * followed by a newline.
2307          */
2308         if (strcmp(tok, "ERROR") == 0) {
2309             tok = strtok(NULL, "\n");
2310             if (tok == NULL)
2311                 tok = "[bogus error packet]";
2312             errstr = newstralloc(errstr, tok);
2313             *response_error = 2;
2314             return;
2315         }
2316
2317
2318         /*
2319          * Regular packets have CONNECT followed by three streams
2320          */
2321         if (strcmp(tok, "CONNECT") == 0) {
2322
2323             /*
2324              * Parse the three stream specifiers out of the packet.
2325              */
2326             for (i = 0; i < NSTREAMS; i++) {
2327                 tok = strtok(NULL, " ");
2328                 if (tok == NULL || strcmp(tok, amidxtaped_streams[i].name) != 0) {
2329                     extra = vstralloc("CONNECT token is \"",
2330                                       tok ? tok : "(null)",
2331                                       "\": expected \"",
2332                                       amidxtaped_streams[i].name,
2333                                       "\"",
2334                                       NULL);
2335                     goto parse_error;
2336                 }
2337                 tok = strtok(NULL, " \n");
2338                 if (tok == NULL || sscanf(tok, "%d", &ports[i]) != 1) {
2339                     extra = vstralloc("CONNECT ",
2340                                       amidxtaped_streams[i].name,
2341                                       " token is \"",
2342                                       tok ? tok : "(null)",
2343                                       "\": expected a port number",
2344                                       NULL);
2345                     goto parse_error;
2346                 }
2347             }
2348             continue;
2349         }
2350
2351         /*
2352          * OPTIONS [options string] '\n'
2353          */
2354         if (strcmp(tok, "OPTIONS") == 0) {
2355             tok = strtok(NULL, "\n");
2356             if (tok == NULL) {
2357                 extra = stralloc("OPTIONS token is missing");
2358                 goto parse_error;
2359             }
2360 /*
2361             while((p = strchr(tok, ';')) != NULL) {
2362                 *p++ = '\0';
2363                 if(strncmp_const(tok, "features=") == 0) {
2364                     tok += sizeof("features=") - 1;
2365                     am_release_feature_set(their_features);
2366                     if((their_features = am_string_to_feature(tok)) == NULL) {
2367                         errstr = newvstralloc(errstr,
2368                                               "OPTIONS: bad features value: ",
2369                                               tok,
2370                                               NULL);
2371                         goto parse_error;
2372                     }
2373                 }
2374                 tok = p;
2375             }
2376 */
2377             continue;
2378         }
2379 /*
2380         extra = vstralloc("next token is \"",
2381                           tok ? tok : "(null)",
2382                           "\": expected \"CONNECT\", \"ERROR\" or \"OPTIONS\"",
2383                           NULL);
2384         goto parse_error;
2385 */
2386     }
2387
2388     /*
2389      * Connect the streams to their remote ports
2390      */
2391     for (i = 0; i < NSTREAMS; i++) {
2392         if (ports[i] == -1)
2393             continue;
2394         amidxtaped_streams[i].fd = security_stream_client(sech, ports[i]);
2395         dbprintf(("amidxtaped_streams[%d].fd = %p\n",i, amidxtaped_streams[i].fd));
2396         if (amidxtaped_streams[i].fd == NULL) {
2397             errstr = newvstralloc(errstr,
2398                         "[could not connect ", amidxtaped_streams[i].name, " stream: ",
2399                         security_geterror(sech), "]", NULL);
2400             goto connect_error;
2401         }
2402     }
2403     /*
2404      * Authenticate the streams
2405      */
2406     for (i = 0; i < NSTREAMS; i++) {
2407         if (amidxtaped_streams[i].fd == NULL)
2408             continue;
2409         if (security_stream_auth(amidxtaped_streams[i].fd) < 0) {
2410             errstr = newvstralloc(errstr,
2411                 "[could not authenticate ", amidxtaped_streams[i].name, " stream: ",
2412                 security_stream_geterror(amidxtaped_streams[i].fd), "]", NULL);
2413             goto connect_error;
2414         }
2415     }
2416
2417     /*
2418      * The CTLFD and DATAFD streams are mandatory.  If we didn't get
2419      * them, complain.
2420      */
2421     if (amidxtaped_streams[CTLFD].fd == NULL) {
2422         errstr = newstralloc(errstr, "[couldn't open CTL streams]");
2423         goto connect_error;
2424     }
2425     if (amidxtaped_streams[DATAFD].fd == NULL) {
2426         errstr = newstralloc(errstr, "[couldn't open DATA streams]");
2427         goto connect_error;
2428     }
2429
2430     /* everything worked */
2431     *response_error = 0;
2432     return;
2433
2434 parse_error:
2435     errstr = newvstralloc(errstr,
2436                           "[parse of reply message failed: ",
2437                           extra ? extra : "(no additional information)",
2438                           "]",
2439                           NULL);
2440     amfree(extra);
2441     *response_error = 2;
2442     return;
2443
2444 connect_error:
2445     stop_amidxtaped();
2446     *response_error = 1;
2447 }
2448
2449 /*
2450  * This is called when everything needs to shut down so event_loop()
2451  * will exit.
2452  */
2453 static void
2454 stop_amidxtaped(void)
2455 {
2456     int i;
2457
2458     for (i = 0; i < NSTREAMS; i++) {
2459         if (amidxtaped_streams[i].fd != NULL) {
2460             security_stream_close(amidxtaped_streams[i].fd);
2461             amidxtaped_streams[i].fd = NULL;
2462         }
2463     }
2464 }
2465
2466 static char* ctl_buffer = NULL;
2467 /* gets a "line" from server and put in server_line */
2468 /* server_line is terminated with \0, \r\n is striped */
2469 /* returns -1 if error */
2470
2471 int
2472 get_amidxtaped_line(void)
2473 {
2474     ssize_t size;
2475     char *newbuf, *s;
2476     void *buf;
2477
2478     amfree(amidxtaped_line);
2479     if (!ctl_buffer)
2480         ctl_buffer = stralloc("");
2481
2482     while (!strstr(ctl_buffer,"\r\n")) {
2483         size = security_stream_read_sync(amidxtaped_streams[CTLFD].fd, &buf);
2484         if(size < 0) {
2485             return -1;
2486         }
2487         else if(size == 0) {
2488             return -1;
2489         }
2490         newbuf = alloc(strlen(ctl_buffer)+size+1);
2491         strncpy(newbuf, ctl_buffer, (size_t)(strlen(ctl_buffer) + size + 1));
2492         memcpy(newbuf+strlen(ctl_buffer), buf, (size_t)size);
2493         newbuf[strlen(ctl_buffer)+size] = '\0';
2494         amfree(ctl_buffer);
2495         ctl_buffer = newbuf;
2496     }
2497
2498     s = strstr(ctl_buffer,"\r\n");
2499     *s = '\0';
2500     newbuf = stralloc(s+2);
2501     amidxtaped_line = stralloc(ctl_buffer);
2502     amfree(ctl_buffer);
2503     ctl_buffer = newbuf;
2504     return 0;
2505 }
2506
2507
2508 static void
2509 read_amidxtaped_data(
2510     void *      cookie,
2511     void *      buf,
2512     ssize_t     size)
2513 {
2514     int fd;
2515
2516     assert(cookie != NULL);
2517
2518     fd = *(int *)cookie;
2519     if (size < 0) {
2520         errstr = newstralloc2(errstr, "amidxtaped read: ",
2521                  security_stream_geterror(amidxtaped_streams[DATAFD].fd));
2522         return;
2523     }
2524
2525     /*
2526      * EOF.  Stop and return.
2527      */
2528     if (size == 0) {
2529         security_stream_close(amidxtaped_streams[DATAFD].fd);
2530         amidxtaped_streams[DATAFD].fd = NULL;
2531         /*
2532          * If the mesg fd has also shut down, then we're done.
2533          */
2534         return;
2535     }
2536
2537     assert(buf != NULL);
2538
2539     /*
2540      * We ignore errors while writing to the index file.
2541      */
2542     (void)fullwrite(fd, buf, (size_t)size);
2543     security_stream_read(amidxtaped_streams[DATAFD].fd, read_amidxtaped_data, cookie);
2544 }
2545
2546 char *
2547 amidxtaped_client_get_security_conf(
2548     char *      string,
2549     void *      arg)
2550 {
2551     (void)arg;  /* Quiet unused parameter warning */
2552
2553     if(!string || !*string)
2554         return(NULL);
2555
2556     if(strcmp(string, "auth")==0) {
2557         return(getconf_str(CNF_AUTH));
2558     }
2559     if(strcmp(string, "ssh_keys")==0) {
2560         return(getconf_str(CNF_SSH_KEYS));
2561     }
2562     return(NULL);
2563 }