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