afb6278a58e9cfa7badda94f21b3615512e11742
[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             g_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                 g_fprintf(stderr,_("timeout waiting for restore\n"));
201                 g_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                 g_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             g_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                             g_printf(_("WARNING: %s is not a directory, "
504                                    "it will be deleted.\n"),
505                                    path);
506                         }
507                     }
508                 }
509                 else if (errno != ENOENT) {
510                     g_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                         g_printf(_("WARNING: All existing files in %s "
528                                "will be deleted.\n"), filename);
529                     }
530                 } else if(S_ISREG(stat_buf.st_mode)) {
531                     g_printf(_("WARNING: Existing file %s will be overwritten\n"),
532                            filename);
533                 } else {
534                     if (add_to_unlink_list(filename)) {
535                         g_printf(_("WARNING: Existing entry %s will be deleted\n"),
536                                filename);
537                     }
538                 }
539             } else if (errno != ENOENT) {
540                 g_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         g_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         g_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         g_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 = newstralloc2(cmd, "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                     g_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                         long long fileno_ = (long long)0;
896                         skip_whitespace(s, ch);
897                         if(ch == '\0' ||
898                            sscanf(s - 1, "%lld", &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                         g_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                         g_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                     g_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                     g_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                     g_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                     g_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         g_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         g_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         g_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         g_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 = newstralloc2(cmd, "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                     g_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                         long long fileno_ = (long long)0;
1209                         skip_whitespace(s, ch);
1210                         if(ch == '\0' ||
1211                            sscanf(s - 1, "%lld", &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                         g_printf(_("System error\n"));
1235                         dbprintf(_("delete_file: (Failed) System error\n"));
1236                         break;
1237                     case  0:
1238                         g_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                     g_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                     g_printf(_("System error\n"));
1266                     dbprintf(_("delete_file: (Failed) System error\n"));
1267                     break;
1268                 case  0:
1269                     g_printf(_("Deleted %s\n"), ditem->path);
1270                     dbprintf(_("delete_file: (Successful) Deleted %s\n"),
1271                               ditem->path);
1272                     break;
1273                 case  1:
1274                     g_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         g_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             g_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             g_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         g_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             g_fprintf(fp, "\t%s\n", that->path);
1343     }
1344
1345     if (file == NULL) {
1346         apclose(fp);
1347     } else {
1348         g_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_device;
1397
1398     get_device = 0;
1399     while (ret < 0) {
1400         if (get_device) {
1401             prompt = _("New device name [?]: ");
1402         } else if (allow_tape && allow_skip) {
1403             prompt = _("Continue [?/Y/n/s/d]? ");
1404         } else if (allow_tape && !allow_skip) {
1405             prompt = _("Continue [?/Y/n/d]? ");
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_device) {
1418                 get_device = 0;
1419                 continue;
1420             }
1421             ret = 0;
1422             break;
1423         }
1424         dbprintf("User prompt: '%s'; response: '%s'\n", prompt, line);
1425
1426         s = line;
1427         while ((ch = *s++) != '\0' && isspace(ch)) {
1428             (void)ch;   /* Quiet empty loop compiler warning */
1429         }
1430         if (ch == '?') {
1431             if (get_device) {
1432                 g_printf(_("Enter a new device name or \"default\"\n"));
1433             } else {
1434                 g_printf(_("Enter \"y\"es to continue, \"n\"o to stop"));
1435                 if(allow_skip) {
1436                     g_printf(_(", \"s\"kip this tape"));
1437                 }
1438                 if(allow_retry) {
1439                     g_printf(_(" or \"r\"etry this tape"));
1440                 }
1441                 if (allow_tape) {
1442                     g_printf(_(" or \"d\" to change to a new device"));
1443                 }
1444                 putchar('\n');
1445             }
1446         } else if (get_device) {
1447             char *tmp = stralloc(tape_server_name);
1448
1449             if (strncmp_const(s - 1, "default") == 0) {
1450                 set_device(tmp, NULL); /* default device, existing host */
1451             } else if (s[-1] != '\0') {
1452                 set_device(tmp, s - 1); /* specified device, existing host */
1453             } else {
1454                 g_printf(_("No change.\n"));
1455             }
1456
1457             amfree(tmp);
1458
1459             get_device = 0;
1460         } else if (ch == '\0' || ch == 'Y' || ch == 'y') {
1461             ret = 1;
1462         } else if (allow_tape && (ch == 'D' || ch == 'd' || ch == 'T' || ch == 't')) {
1463             get_device = 1; /* ('T' and 't' are for backward-compatibility) */
1464         } else if (ch == 'N' || ch == 'n') {
1465             ret = 0;
1466         } else if (allow_retry && (ch == 'R' || ch == 'r')) {
1467             ret = RETRY_TAPE;
1468         } else if (allow_skip && (ch == 'S' || ch == 's')) {
1469             ret = SKIP_TAPE;
1470         }
1471     }
1472     /*@ignore@*/
1473     amfree(line);
1474     /*@end@*/
1475     return ret;
1476 }
1477
1478 static void
1479 send_to_tape_server(
1480     security_stream_t * stream,
1481     char *              cmd)
1482 {
1483     char *msg = stralloc2(cmd, "\r\n");
1484
1485     if (security_stream_write(stream, msg, strlen(msg)) < 0)
1486     {
1487         error(_("Error writing to tape server"));
1488         exit(101);
1489         /*NOTREACHED*/
1490     }
1491     amfree(msg);
1492 }
1493
1494
1495 /* start up connection to tape server and set commands to initiate
1496    transfer of dump image.
1497    Return tape server socket on success, -1 on error. */
1498 static int
1499 extract_files_setup(
1500     char *      label,
1501     off_t       fsf)
1502 {
1503     char *disk_regex = NULL;
1504     char *host_regex = NULL;
1505     char *clean_datestamp, *ch, *ch1;
1506     char *tt = NULL;
1507     char *req;
1508     int response_error;
1509
1510     amidxtaped_secdrv = security_getdriver(authopt);
1511     if (amidxtaped_secdrv == NULL) {
1512         error(_("no '%s' security driver available for host '%s'"),
1513               authopt, tape_server_name);
1514     }
1515
1516     /* We assume that amidxtaped support fe_amidxtaped_options_features */
1517     /*                               and fe_amidxtaped_options_auth     */
1518     /* We should send a noop to really know                             */
1519     req = vstralloc("SERVICE amidxtaped\n",
1520                     "OPTIONS ", "features=", our_features_string, ";",
1521                                 "auth=", authopt, ";",
1522                     "\n", NULL);
1523     protocol_sendreq(tape_server_name, amidxtaped_secdrv,
1524                      generic_client_get_security_conf, req, STARTUP_TIMEOUT,
1525                      amidxtaped_response, &response_error);
1526     amfree(req);
1527     protocol_run();
1528     if(response_error != 0) {
1529         return -1;
1530     }
1531
1532     disk_regex = alloc(strlen(disk_name) * 2 + 3);
1533
1534     ch = disk_name;
1535     ch1 = disk_regex;
1536
1537     /* we want to force amrestore to only match disk_name exactly */
1538     *(ch1++) = '^';
1539
1540     /* We need to escape some characters first... NT compatibilty crap */
1541     for (; *ch != 0; ch++, ch1++) {
1542         switch (*ch) {     /* done this way in case there are more */
1543         case '$':
1544             *(ch1++) = '\\';
1545             /* no break; we do want to fall through... */
1546         default:
1547             *ch1 = *ch;
1548         }
1549     }
1550
1551     /* we want to force amrestore to only match disk_name exactly */
1552     *(ch1++) = '$';
1553
1554     *ch1 = '\0';
1555
1556     host_regex = alloc(strlen(dump_hostname) * 2 + 3);
1557
1558     ch = dump_hostname;
1559     ch1 = host_regex;
1560
1561     /* we want to force amrestore to only match dump_hostname exactly */
1562     *(ch1++) = '^';
1563
1564     /* We need to escape some characters first... NT compatibilty crap */
1565     for (; *ch != 0; ch++, ch1++) {
1566         switch (*ch) {     /* done this way in case there are more */
1567         case '$':
1568             *(ch1++) = '\\';
1569             /* no break; we do want to fall through... */
1570         default:
1571             *ch1 = *ch;
1572         }
1573     }
1574
1575     /* we want to force amrestore to only match dump_hostname exactly */
1576     *(ch1++) = '$';
1577
1578     *ch1 = '\0';
1579
1580     clean_datestamp = stralloc(dump_datestamp);
1581     for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) {
1582         if(*ch1 != '-') {
1583             *ch = *ch1;
1584             ch++;
1585         }
1586     }
1587     *ch = '\0';
1588     /* push our feature list off to the tape server */
1589     /* XXX assumes that index server and tape server are equivalent, ew */
1590
1591     if(am_has_feature(indexsrv_features, fe_amidxtaped_exchange_features)){
1592         tt = newstralloc2(tt, "FEATURES=", our_features_string);
1593         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1594         get_amidxtaped_line();
1595         if(strncmp_const(amidxtaped_line,"FEATURES=") == 0) {
1596             tapesrv_features = am_string_to_feature(amidxtaped_line+9);
1597         } else {
1598             g_fprintf(stderr, _("amrecover - expecting FEATURES line from amidxtaped\n"));
1599             stop_amidxtaped();
1600             amfree(disk_regex);
1601             amfree(host_regex);
1602             amfree(clean_datestamp);
1603             return -1;
1604         }
1605         am_release_feature_set(tapesrv_features);
1606     }
1607
1608
1609     if(am_has_feature(indexsrv_features, fe_amidxtaped_header) &&
1610        am_has_feature(indexsrv_features, fe_amidxtaped_device) &&
1611        am_has_feature(indexsrv_features, fe_amidxtaped_host) &&
1612        am_has_feature(indexsrv_features, fe_amidxtaped_disk) &&
1613        am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) {
1614
1615         if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) {
1616             tt = newstralloc2(tt, "CONFIG=", config_name);
1617             send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1618         }
1619         if(am_has_feature(indexsrv_features, fe_amidxtaped_label) &&
1620            label && label[0] != '/') {
1621             tt = newstralloc2(tt,"LABEL=",label);
1622             send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1623         }
1624         if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) {
1625             char v_fsf[100];
1626             g_snprintf(v_fsf, 99, "%lld", (long long)fsf);
1627             tt = newstralloc2(tt, "FSF=",v_fsf);
1628             send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1629         }
1630         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "HEADER");
1631         tt = newstralloc2(tt, "DEVICE=", dump_device_name);
1632         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1633         tt = newstralloc2(tt, "HOST=", host_regex);
1634         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1635         tt = newstralloc2(tt, "DISK=", disk_regex);
1636         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1637         tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp);
1638         send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
1639         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "END");
1640         amfree(tt);
1641     }
1642     else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) {
1643         /* send to the tape server what tape file we want */
1644         /* 6 args:
1645          *   "-h"
1646          *   "-p"
1647          *   "tape device"
1648          *   "hostname"
1649          *   "diskname"
1650          *   "datestamp"
1651          */
1652         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "6");
1653         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "-h");
1654         send_to_tape_server(amidxtaped_streams[CTLFD].fd, "-p");
1655         send_to_tape_server(amidxtaped_streams[CTLFD].fd, dump_device_name);
1656         send_to_tape_server(amidxtaped_streams[CTLFD].fd, host_regex);
1657         send_to_tape_server(amidxtaped_streams[CTLFD].fd, disk_regex);
1658         send_to_tape_server(amidxtaped_streams[CTLFD].fd, clean_datestamp);
1659
1660         dbprintf(_("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n"),
1661                   dump_device_name, host_regex, disk_regex, clean_datestamp);
1662     }
1663
1664     amfree(disk_regex);
1665     amfree(host_regex);
1666     amfree(clean_datestamp);
1667
1668     return 0;
1669 }
1670
1671
1672 /*
1673  * Reads the first block of a tape file.
1674  */
1675
1676 void
1677 read_file_header(
1678     char *      buffer,
1679     dumpfile_t *file,
1680     size_t      buflen,
1681     int         tapedev)
1682 {
1683     ssize_t bytes_read;
1684     bytes_read = read_buffer(tapedev, buffer, buflen, READ_TIMEOUT);
1685     if(bytes_read < 0) {
1686         error(_("error reading header (%s), check amidxtaped.*.debug on server"),
1687               strerror(errno));
1688         /*NOTREACHED*/
1689     }
1690
1691     if((size_t)bytes_read < buflen) {
1692         g_fprintf(stderr, plural(_("%s: short block %d byte\n"),
1693                                _("%s: short block %d bytes\n"), bytes_read),
1694                 get_pname(), (int)bytes_read);
1695         print_header(stdout, file);
1696         error(_("Can't read file header"));
1697         /*NOTREACHED*/
1698     }
1699
1700     /* bytes_read == buflen */
1701     parse_file_header(buffer, file, (size_t)bytes_read);
1702 }
1703
1704 enum dumptypes {
1705         IS_UNKNOWN,
1706         IS_DUMP,
1707         IS_GNUTAR,
1708         IS_TAR,
1709         IS_SAMBA,
1710         IS_SAMBA_TAR,
1711         IS_BACKUP_API
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         if (strcmp(file.program, "BACKUP") == 0)
1757             dumptype = IS_BACKUP_API;
1758 #ifdef GNUTAR
1759         if (strcmp(file.program, GNUTAR) == 0)
1760             dumptype = IS_GNUTAR;
1761 #endif
1762
1763         if (dumptype == IS_UNKNOWN) {
1764             len_program = strlen(file.program);
1765             if(len_program >= 3 &&
1766                strcmp(&file.program[len_program-3],"tar") == 0)
1767                 dumptype = IS_TAR;
1768         }
1769
1770 #ifdef SAMBA_CLIENT
1771         if (dumptype == IS_UNKNOWN && strcmp(file.program, SAMBA_CLIENT) ==0) {
1772             if (samba_extract_method == SAMBA_TAR)
1773               dumptype = IS_SAMBA_TAR;
1774             else
1775               dumptype = IS_SAMBA;
1776         }
1777 #endif
1778     }
1779
1780     /* form the arguments to restore */
1781     files_off_tape = length_of_tape_list(elist);
1782     switch (dumptype) {
1783     case IS_SAMBA:
1784 #ifdef SAMBA_CLIENT
1785         extra_params = 10;
1786         break;
1787 #endif
1788     case IS_TAR:
1789     case IS_GNUTAR:
1790         extra_params = 4;
1791         break;
1792     case IS_SAMBA_TAR:
1793         extra_params = 3;
1794         break;
1795     case IS_UNKNOWN:
1796     case IS_DUMP:
1797 #ifdef AIX_BACKUP
1798         extra_params = 2;
1799 #else
1800 #if defined(XFSDUMP)
1801         if (strcmp(file.program, XFSDUMP) == 0) {
1802             extra_params = 4 + files_off_tape;
1803         } else
1804 #endif
1805         {
1806         extra_params = 4;
1807         }
1808 #endif
1809         break;
1810     case IS_BACKUP_API:
1811         extra_params = 5;
1812         break;
1813     }
1814     restore_args = (char **)alloc((size_t)((extra_params + files_off_tape + 1)
1815                                   * sizeof(char *)));
1816     switch(dumptype) {
1817     case IS_SAMBA:
1818 #ifdef SAMBA_CLIENT
1819         restore_args[j++] = stralloc("smbclient");
1820         smbpass = findpass(file.disk, &domain);
1821         if (smbpass) {
1822             restore_args[j++] = stralloc(file.disk);
1823             passwd_field=j;
1824             restore_args[j++] = stralloc("-U");
1825             restore_args[j++] = smbpass;
1826             if (domain) {
1827                 restore_args[j++] = stralloc("-W");
1828                 restore_args[j++] = stralloc(domain);
1829             } else
1830                 extra_params -= 2;
1831         } else
1832             extra_params -= 6;
1833         restore_args[j++] = stralloc("-d0");
1834         restore_args[j++] = stralloc("-Tx");
1835         restore_args[j++] = stralloc("-");      /* data on stdin */
1836         break;
1837 #endif
1838     case IS_TAR:
1839     case IS_GNUTAR:
1840         restore_args[j++] = stralloc("tar");
1841         restore_args[j++] = stralloc("--numeric-owner");
1842         restore_args[j++] = stralloc("-xpGvf");
1843         restore_args[j++] = stralloc("-");      /* data on stdin */
1844         break;
1845     case IS_SAMBA_TAR:
1846         restore_args[j++] = stralloc("tar");
1847         restore_args[j++] = stralloc("-xpvf");
1848         restore_args[j++] = stralloc("-");      /* data on stdin */
1849         break;
1850     case IS_UNKNOWN:
1851     case IS_DUMP:
1852         restore_args[j++] = stralloc("restore");
1853 #ifdef AIX_BACKUP
1854         restore_args[j++] = stralloc("-xB");
1855 #else
1856 #if defined(XFSDUMP)
1857         if (strcmp(file.program, XFSDUMP) == 0) {
1858             restore_args[j++] = stralloc("-v");
1859             restore_args[j++] = stralloc("silent");
1860         } else
1861 #endif
1862 #if defined(VDUMP)
1863         if (strcmp(file.program, VDUMP) == 0) {
1864             restore_args[j++] = stralloc("xf");
1865             restore_args[j++] = stralloc("-");  /* data on stdin */
1866         } else
1867 #endif
1868         {
1869         restore_args[j++] = stralloc("xbf");
1870         restore_args[j++] = stralloc("2");      /* read in units of 1K */
1871         restore_args[j++] = stralloc("-");      /* data on stdin */
1872         }
1873 #endif
1874         break;
1875     case IS_BACKUP_API:
1876         restore_args[j++] = stralloc(file.dumper);
1877         restore_args[j++] = stralloc("restore");
1878         restore_args[j++] = stralloc("--config");
1879         restore_args[j++] = stralloc(config_name);
1880         restore_args[j++] = stralloc("--disk");
1881         restore_args[j++] = stralloc(file.disk);
1882         break;
1883     }
1884   
1885     for (i = 0, fn = elist->files; i < files_off_tape; i++, fn = fn->next)
1886     {
1887         switch (dumptype) {
1888         case IS_BACKUP_API:
1889         case IS_TAR:
1890         case IS_GNUTAR:
1891         case IS_SAMBA_TAR:
1892         case IS_SAMBA:
1893             if (strcmp(fn->path, "/") == 0)
1894                 restore_args[j++] = stralloc(".");
1895             else
1896                 restore_args[j++] = stralloc2(".", fn->path);
1897             break;
1898         case IS_UNKNOWN:
1899         case IS_DUMP:
1900 #if defined(XFSDUMP)
1901             if (strcmp(file.program, XFSDUMP) == 0) {
1902                 /*
1903                  * xfsrestore needs a -s option before each file to be
1904                  * restored, and also wants them to be relative paths.
1905                  */
1906                 restore_args[j++] = stralloc("-s");
1907                 restore_args[j++] = stralloc(fn->path + 1);
1908             } else
1909 #endif
1910             {
1911             restore_args[j++] = stralloc(fn->path);
1912             }
1913             break;
1914         }
1915     }
1916 #if defined(XFSDUMP)
1917     if (strcmp(file.program, XFSDUMP) == 0) {
1918         restore_args[j++] = stralloc("-");
1919         restore_args[j++] = stralloc(".");
1920     }
1921 #endif
1922     restore_args[j] = NULL;
1923
1924     switch (dumptype) {
1925     case IS_SAMBA:
1926 #ifdef SAMBA_CLIENT
1927         cmd = stralloc(SAMBA_CLIENT);
1928         break;
1929 #else
1930         /* fall through to ... */
1931 #endif
1932     case IS_TAR:
1933     case IS_GNUTAR:
1934     case IS_SAMBA_TAR:
1935 #ifndef GNUTAR
1936         g_fprintf(stderr, _("warning: GNUTAR program not available.\n"));
1937         cmd = stralloc("tar");
1938 #else
1939         cmd = stralloc(GNUTAR);
1940 #endif
1941         break;
1942     case IS_UNKNOWN:
1943     case IS_DUMP:
1944         cmd = NULL;
1945 #if defined(DUMP)
1946         if (strcmp(file.program, DUMP) == 0) {
1947             cmd = stralloc(RESTORE);
1948         }
1949 #endif
1950 #if defined(VDUMP)
1951         if (strcmp(file.program, VDUMP) == 0) {
1952             cmd = stralloc(VRESTORE);
1953         }
1954 #endif
1955 #if defined(VXDUMP)
1956         if (strcmp(file.program, VXDUMP) == 0) {
1957             cmd = stralloc(VXRESTORE);
1958         }
1959 #endif
1960 #if defined(XFSDUMP)
1961         if (strcmp(file.program, XFSDUMP) == 0) {
1962             cmd = stralloc(XFSRESTORE);
1963         }
1964 #endif
1965         if (cmd == NULL) {
1966             g_fprintf(stderr, _("warning: restore program for %s not available.\n"),
1967                     file.program);
1968             cmd = stralloc("restore");
1969         }
1970         break;
1971     case IS_BACKUP_API:
1972         cmd = vstralloc(DUMPER_DIR, "/", file.dumper, NULL);
1973         break;
1974     }
1975     if (cmd) {
1976         dbprintf(_("Exec'ing %s with arguments:\n"), cmd);
1977         for (i = 0; i < j; i++) {
1978             if( i == passwd_field)
1979                 dbprintf("\tXXXXX\n");
1980             else
1981                 dbprintf(_("\t%s\n"), restore_args[i]);
1982         }
1983         safe_fd(-1, 0);
1984         (void)execv(cmd, restore_args);
1985         /* only get here if exec failed */
1986         save_errno = errno;
1987         for (i = 0; i < j; i++) {
1988             amfree(restore_args[i]);
1989         }
1990         amfree(restore_args);
1991         errno = save_errno;
1992         perror(_("amrecover couldn't exec"));
1993         g_fprintf(stderr, _(" problem executing %s\n"), cmd);
1994         amfree(cmd);
1995     }
1996     exit(1);
1997     /*NOT REACHED */
1998 }
1999
2000 /*
2001  * Interpose something between the process writing out the dump (writing it to
2002  * some extraction program, really) and the socket from which we're reading, so
2003  * that we can do things like prompt for human interaction for multiple tapes.
2004  */
2005 int
2006 writer_intermediary(
2007     EXTRACT_LIST *      elist)
2008 {
2009     int child_pipe[2];
2010     pid_t pid;
2011     amwait_t extractor_status;
2012
2013     if(pipe(child_pipe) == -1) {
2014         error(_("extract_list - error setting up pipe to extractor: %s\n"),
2015               strerror(errno));
2016         /*NOTREACHED*/
2017     }
2018
2019     /* okay, ready to extract. fork a child to do the actual work */
2020     if ((pid = fork()) == 0) {
2021         /* this is the child process */
2022         /* never gets out of this clause */
2023         aclose(child_pipe[1]);
2024         extract_files_child(child_pipe[0], elist);
2025         /*NOTREACHED*/
2026     }
2027
2028     /* This is the parent */
2029     if (pid == -1) {
2030         g_printf(_("writer_intermediary - error forking child"));
2031         return -1;
2032     }
2033
2034     aclose(child_pipe[0]);
2035
2036     security_stream_read(amidxtaped_streams[DATAFD].fd,
2037                          read_amidxtaped_data, &(child_pipe[1]));
2038
2039     while(get_amidxtaped_line() >= 0) {
2040         char desired_tape[MAX_TAPE_LABEL_BUF];
2041                 
2042         /* if prompted for a tape, relay said prompt to the user */
2043         if(sscanf(amidxtaped_line, "FEEDME %132s\n", desired_tape) == 1) {
2044             int done;
2045             g_printf(_("Load tape %s now\n"), desired_tape);
2046             dbprintf(_("Requesting tape %s from user\n"), desired_tape);
2047             done = okay_to_continue(am_has_feature(indexsrv_features,
2048                                                    fe_amrecover_feedme_tape),
2049                                     0, 0);
2050             if (done == 1) {
2051                 if (am_has_feature(indexsrv_features,
2052                                    fe_amrecover_feedme_tape)) {
2053                     char *reply = stralloc2("TAPE ", tape_device_name);
2054                     send_to_tape_server(amidxtaped_streams[CTLFD].fd, reply);
2055                     amfree(reply);
2056                 } else {
2057                     send_to_tape_server(amidxtaped_streams[CTLFD].fd, "OK");
2058                 }
2059             } else {
2060                 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "ERROR");
2061                 break;
2062             }
2063         } else if(strncmp_const(amidxtaped_line, "MESSAGE ") == 0) {
2064             g_printf("%s\n",&amidxtaped_line[8]);
2065         } else {
2066             g_fprintf(stderr, _("Strange message from tape server: %s"),
2067                     amidxtaped_line);
2068             break;
2069         }
2070     }
2071
2072     /* CTL might be close before DATA */
2073     event_loop(0);
2074     aclose(child_pipe[1]);
2075
2076     waitpid(pid, &extractor_status, 0);
2077     if(WEXITSTATUS(extractor_status) != 0){
2078         int ret = WEXITSTATUS(extractor_status);
2079         if(ret == 255) ret = -1;
2080         g_printf(_("Extractor child exited with status %d\n"), ret);
2081         return -1;
2082     }
2083     return(0);
2084 }
2085
2086 /* exec restore to do the actual restoration */
2087
2088 /* does the actual extraction of files */
2089 /*
2090  * The original design had the dump image being returned exactly as it
2091  * appears on the tape, and this routine getting from the index server
2092  * whether or not it is compressed, on the assumption that the tape
2093  * server may not know how to uncompress it. But
2094  * - Amrestore can't do that. It returns either compressed or uncompressed
2095  * (always). Amrestore assumes it can uncompress files. It is thus a good
2096  * idea to run the tape server on a machine with gzip.
2097  * - The information about compression in the disklist is really only
2098  * for future dumps. It is possible to change compression on a drive
2099  * so the information in the disklist may not necessarily relate to
2100  * the dump image on the tape.
2101  *   Consequently the design was changed to assuming that amrestore can
2102  * uncompress any dump image and have it return an uncompressed file
2103  * always.
2104  */
2105 void
2106 extract_files(void)
2107 {
2108     EXTRACT_LIST *elist;
2109     char * cwd;
2110     char *l;
2111     int first;
2112     int otc;
2113     tapelist_t *tlist = NULL, *a_tlist;
2114
2115     if (!is_extract_list_nonempty())
2116     {
2117         g_printf(_("Extract list empty - No files to extract!\n"));
2118         return;
2119     }
2120
2121     clean_extract_list();
2122
2123     /* get tape device name from index server if none specified */
2124     if (tape_server_name == NULL) {
2125         tape_server_name = newstralloc(tape_server_name, server_name);
2126     }
2127     if (tape_device_name == NULL) {
2128         if (send_command("TAPE") == -1)
2129             exit(1);
2130         if (get_reply_line() == -1)
2131             exit(1);
2132         l = reply_line();
2133         if (!server_happy())
2134         {
2135             g_printf("%s\n", l);
2136             exit(1);
2137         }
2138         /* skip reply number */
2139         tape_device_name = newstralloc(tape_device_name, l+4);
2140     }
2141
2142     if (strcmp(tape_device_name, "/dev/null") == 0)
2143     {
2144         g_printf(_("amrecover: warning: using %s as the tape device will not work\n"),
2145                tape_device_name);
2146     }
2147
2148     first=1;
2149     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) {
2150         if(elist->tape[0]!='/') {
2151             if(first) {
2152                 g_printf(_("\nExtracting files using tape drive %s on host %s.\n"),
2153                         tape_device_name, tape_server_name);
2154                 g_printf(_("The following tapes are needed:"));
2155                 first=0;
2156             }
2157             else
2158                 g_printf("                               ");
2159             tlist = unmarshal_tapelist_str(elist->tape); 
2160             for(a_tlist = tlist ; a_tlist != NULL; a_tlist = a_tlist->next)
2161                 g_printf(" %s", a_tlist->label);
2162             g_printf("\n");
2163             free_tapelist(tlist);
2164         }
2165     }
2166     first=1;
2167     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) {
2168         if(elist->tape[0]=='/') {
2169             if(first) {
2170                 g_printf(_("\nExtracting files from holding disk on host %s.\n"),
2171                         tape_server_name);
2172                 g_printf(_("The following files are needed:"));
2173                 first=0;
2174             }
2175             else
2176                 g_printf("                               ");
2177             tlist = unmarshal_tapelist_str(elist->tape); 
2178             for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next)
2179                 g_printf(" %s", a_tlist->label);
2180             g_printf("\n");
2181             free_tapelist(tlist);
2182         }
2183     }
2184     g_printf("\n");
2185
2186     cwd = g_get_current_dir();
2187     if (cwd == NULL) {
2188         perror(_("extract_list: Current working directory unavailable"));
2189         exit(1);
2190     }
2191
2192     g_printf(_("Restoring files into directory %s\n"), cwd);
2193     check_file_overwrite(cwd);
2194
2195 #ifdef SAMBA_CLIENT
2196     if (samba_extract_method == SAMBA_SMBCLIENT)
2197       g_printf(_("(unless it is a Samba backup, that will go through to the SMB server)\n"));
2198 #endif
2199     dbprintf(_("Checking with user before restoring into directory %s\n"), cwd);
2200     if (!okay_to_continue(0,0,0)) {
2201         amfree(cwd);
2202         return;
2203     }
2204     g_printf("\n");
2205
2206     if (!do_unlink_list()) {
2207         g_fprintf(stderr, _("Can't recover because I can't cleanup the cwd (%s)\n"),
2208                 cwd);
2209         amfree(cwd);
2210         return;
2211     }
2212     free_unlink_list();
2213
2214     while ((elist = first_tape_list()) != NULL)
2215     {
2216         if(elist->tape[0]=='/') {
2217             dump_device_name = newstralloc(dump_device_name, elist->tape);
2218             g_printf(_("Extracting from file "));
2219             tlist = unmarshal_tapelist_str(dump_device_name); 
2220             for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next)
2221                 g_printf(" %s", a_tlist->label);
2222             g_printf("\n");
2223             free_tapelist(tlist);
2224         }
2225         else {
2226             g_printf(_("Extracting files using tape drive %s on host %s.\n"),
2227                    tape_device_name, tape_server_name);
2228             tlist = unmarshal_tapelist_str(elist->tape); 
2229             g_printf(_("Load tape %s now\n"), tlist->label);
2230             dbprintf(_("Requesting tape %s from user\n"), tlist->label);
2231             free_tapelist(tlist);
2232             otc = okay_to_continue(1,1,0);
2233             if (otc == 0)
2234                 return;
2235             else if (otc == SKIP_TAPE) {
2236                 delete_tape_list(elist); /* skip this tape */
2237                 continue;
2238             }
2239             dump_device_name = newstralloc(dump_device_name, tape_device_name);
2240         }
2241         dump_datestamp = newstralloc(dump_datestamp, elist->date);
2242
2243         /* connect to the tape handler daemon on the tape drive server */
2244         if ((extract_files_setup(elist->tape, elist->fileno)) == -1)
2245         {
2246             g_fprintf(stderr, _("amrecover - can't talk to tape server: %s\n"),
2247                     errstr);
2248             return;
2249         }
2250
2251         /* if the server have fe_amrecover_feedme_tape, it has asked for
2252          * the tape itself, even if the restore didn't succeed, we should
2253          * remove it.
2254          */
2255         if(writer_intermediary(elist) == 0 ||
2256            am_has_feature(indexsrv_features, fe_amrecover_feedme_tape))
2257             delete_tape_list(elist);    /* tape done so delete from list */
2258
2259         stop_amidxtaped();
2260     }
2261 }
2262
2263 static void
2264 amidxtaped_response(
2265     void *              datap,
2266     pkt_t *             pkt,
2267     security_handle_t * sech)
2268 {
2269     int ports[NSTREAMS], *response_error = datap, i;
2270     char *p;
2271     char *tok;
2272     char *extra = NULL;
2273
2274     assert(response_error != NULL);
2275     assert(sech != NULL);
2276     memset(ports, -1, SIZEOF(ports));
2277
2278     if (pkt == NULL) {
2279         errstr = newvstrallocf(errstr, _("[request failed: %s]"), security_geterror(sech));
2280         *response_error = 1;
2281         return;
2282     }
2283     security_close_connection(sech, dump_hostname);
2284
2285     if (pkt->type == P_NAK) {
2286 #if defined(PACKET_DEBUG)
2287         g_fprintf(stderr, _("got nak response:\n----\n%s\n----\n\n"), pkt->body);
2288 #endif
2289
2290         tok = strtok(pkt->body, " ");
2291         if (tok == NULL || strcmp(tok, "ERROR") != 0)
2292             goto bad_nak;
2293
2294         tok = strtok(NULL, "\n");
2295         if (tok != NULL) {
2296             errstr = newvstralloc(errstr, "NAK: ", tok, NULL);
2297             *response_error = 1;
2298         } else {
2299 bad_nak:
2300             errstr = newstralloc(errstr, "request NAK");
2301             *response_error = 2;
2302         }
2303         return;
2304     }
2305
2306     if (pkt->type != P_REP) {
2307         errstr = newvstrallocf(errstr, _("received strange packet type %s: %s"),
2308                               pkt_type2str(pkt->type), pkt->body);
2309         *response_error = 1;
2310         return;
2311     }
2312
2313 #if defined(PACKET_DEBUG)
2314     g_fprintf(stderr, _("got response:\n----\n%s\n----\n\n"), pkt->body);
2315 #endif
2316
2317     for(i = 0; i < NSTREAMS; i++) {
2318         ports[i] = -1;
2319         amidxtaped_streams[i].fd = NULL;
2320     }
2321
2322     p = pkt->body;
2323     while((tok = strtok(p, " \n")) != NULL) {
2324         p = NULL;
2325
2326         /*
2327          * Error response packets have "ERROR" followed by the error message
2328          * followed by a newline.
2329          */
2330         if (strcmp(tok, "ERROR") == 0) {
2331             tok = strtok(NULL, "\n");
2332             if (tok == NULL)
2333                 tok = _("[bogus error packet]");
2334             errstr = newstralloc(errstr, tok);
2335             *response_error = 2;
2336             return;
2337         }
2338
2339
2340         /*
2341          * Regular packets have CONNECT followed by three streams
2342          */
2343         if (strcmp(tok, "CONNECT") == 0) {
2344
2345             /*
2346              * Parse the three stream specifiers out of the packet.
2347              */
2348             for (i = 0; i < NSTREAMS; i++) {
2349                 tok = strtok(NULL, " ");
2350                 if (tok == NULL || strcmp(tok, amidxtaped_streams[i].name) != 0) {
2351                     extra = vstrallocf(_("CONNECT token is \"%s\": expected \"%s\""),
2352                                       tok ? tok : "(null)",
2353                                       amidxtaped_streams[i].name);
2354                     goto parse_error;
2355                 }
2356                 tok = strtok(NULL, " \n");
2357                 if (tok == NULL || sscanf(tok, "%d", &ports[i]) != 1) {
2358                     extra = vstrallocf(_("CONNECT %s token is \"%s\": expected a port number"),
2359                                       amidxtaped_streams[i].name,
2360                                       tok ? tok : "(null)");
2361                     goto parse_error;
2362                 }
2363             }
2364             continue;
2365         }
2366
2367         /*
2368          * OPTIONS [options string] '\n'
2369          */
2370         if (strcmp(tok, "OPTIONS") == 0) {
2371             tok = strtok(NULL, "\n");
2372             if (tok == NULL) {
2373                 extra = stralloc(_("OPTIONS token is missing"));
2374                 goto parse_error;
2375             }
2376 /*
2377             while((p = strchr(tok, ';')) != NULL) {
2378                 *p++ = '\0';
2379                 if(strncmp_const(tok, "features=") == 0) {
2380                     tok += sizeof("features=") - 1;
2381                     am_release_feature_set(their_features);
2382                     if((their_features = am_string_to_feature(tok)) == NULL) {
2383                         errstr = newvstralloc(errstr,
2384                                               _("OPTIONS: bad features value: "),
2385                                               tok,
2386                                               NULL);
2387                         goto parse_error;
2388                     }
2389                 }
2390                 tok = p;
2391             }
2392 */
2393             continue;
2394         }
2395 /*
2396         extra = vstrallocf("next token is \"%s\": expected \"CONNECT\", \"ERROR\" or \"OPTIONS\""),
2397                           tok ? tok : _("(null)"));
2398         goto parse_error;
2399 */
2400     }
2401
2402     /*
2403      * Connect the streams to their remote ports
2404      */
2405     for (i = 0; i < NSTREAMS; i++) {
2406         if (ports[i] == -1)
2407             continue;
2408         amidxtaped_streams[i].fd = security_stream_client(sech, ports[i]);
2409         dbprintf(_("amidxtaped_streams[%d].fd = %p\n"),i, amidxtaped_streams[i].fd);
2410         if (amidxtaped_streams[i].fd == NULL) {
2411             errstr = newvstrallocf(errstr,\
2412                         _("[could not connect %s stream: %s]"),
2413                         amidxtaped_streams[i].name,
2414                         security_geterror(sech));
2415             goto connect_error;
2416         }
2417     }
2418     /*
2419      * Authenticate the streams
2420      */
2421     for (i = 0; i < NSTREAMS; i++) {
2422         if (amidxtaped_streams[i].fd == NULL)
2423             continue;
2424         if (security_stream_auth(amidxtaped_streams[i].fd) < 0) {
2425             errstr = newvstrallocf(errstr,
2426                 _("[could not authenticate %s stream: %s]"),
2427                 amidxtaped_streams[i].name,
2428                 security_stream_geterror(amidxtaped_streams[i].fd));
2429             goto connect_error;
2430         }
2431     }
2432
2433     /*
2434      * The CTLFD and DATAFD streams are mandatory.  If we didn't get
2435      * them, complain.
2436      */
2437     if (amidxtaped_streams[CTLFD].fd == NULL) {
2438         errstr = newvstrallocf(errstr, _("[couldn't open CTL streams]"));
2439         goto connect_error;
2440     }
2441     if (amidxtaped_streams[DATAFD].fd == NULL) {
2442         errstr = newvstrallocf(errstr, _("[couldn't open DATA streams]"));
2443         goto connect_error;
2444     }
2445
2446     /* everything worked */
2447     *response_error = 0;
2448     return;
2449
2450 parse_error:
2451     if (extra) {
2452         errstr = newvstrallocf(errstr,
2453                           _("[parse of reply message failed: %s]"), extra);
2454     } else {
2455         errstr = newvstrallocf(errstr,
2456                           _("[parse of reply message failed: (no additional information)"));
2457     }
2458     amfree(extra);
2459     *response_error = 2;
2460     return;
2461
2462 connect_error:
2463     stop_amidxtaped();
2464     *response_error = 1;
2465 }
2466
2467 /*
2468  * This is called when everything needs to shut down so event_loop()
2469  * will exit.
2470  */
2471 static void
2472 stop_amidxtaped(void)
2473 {
2474     int i;
2475
2476     for (i = 0; i < NSTREAMS; i++) {
2477         if (amidxtaped_streams[i].fd != NULL) {
2478             security_stream_close(amidxtaped_streams[i].fd);
2479             amidxtaped_streams[i].fd = NULL;
2480         }
2481     }
2482 }
2483
2484 static char* ctl_buffer = NULL;
2485 /* gets a "line" from server and put in server_line */
2486 /* server_line is terminated with \0, \r\n is striped */
2487 /* returns -1 if error */
2488
2489 int
2490 get_amidxtaped_line(void)
2491 {
2492     ssize_t size;
2493     char *newbuf, *s;
2494     void *buf;
2495
2496     amfree(amidxtaped_line);
2497     if (!ctl_buffer)
2498         ctl_buffer = stralloc("");
2499
2500     while (!strstr(ctl_buffer,"\r\n")) {
2501         size = security_stream_read_sync(amidxtaped_streams[CTLFD].fd, &buf);
2502         if(size < 0) {
2503             return -1;
2504         }
2505         else if(size == 0) {
2506             return -1;
2507         }
2508         newbuf = alloc(strlen(ctl_buffer)+size+1);
2509         strncpy(newbuf, ctl_buffer, (size_t)(strlen(ctl_buffer) + size + 1));
2510         memcpy(newbuf+strlen(ctl_buffer), buf, (size_t)size);
2511         newbuf[strlen(ctl_buffer)+size] = '\0';
2512         amfree(ctl_buffer);
2513         ctl_buffer = newbuf;
2514     }
2515
2516     s = strstr(ctl_buffer,"\r\n");
2517     *s = '\0';
2518     newbuf = stralloc(s+2);
2519     amidxtaped_line = stralloc(ctl_buffer);
2520     amfree(ctl_buffer);
2521     ctl_buffer = newbuf;
2522     return 0;
2523 }
2524
2525
2526 static void
2527 read_amidxtaped_data(
2528     void *      cookie,
2529     void *      buf,
2530     ssize_t     size)
2531 {
2532     int fd;
2533
2534     assert(cookie != NULL);
2535
2536     fd = *(int *)cookie;
2537     if (size < 0) {
2538         errstr = newstralloc2(errstr, _("amidxtaped read: "),
2539                  security_stream_geterror(amidxtaped_streams[DATAFD].fd));
2540         return;
2541     }
2542
2543     /*
2544      * EOF.  Stop and return.
2545      */
2546     if (size == 0) {
2547         security_stream_close(amidxtaped_streams[DATAFD].fd);
2548         amidxtaped_streams[DATAFD].fd = NULL;
2549         /*
2550          * If the mesg fd has also shut down, then we're done.
2551          */
2552         return;
2553     }
2554
2555     assert(buf != NULL);
2556
2557     /*
2558      * We ignore errors while writing to the index file.
2559      */
2560     (void)fullwrite(fd, buf, (size_t)size);
2561     security_stream_read(amidxtaped_streams[DATAFD].fd, read_amidxtaped_data, cookie);
2562 }
2563
2564 char *
2565 amidxtaped_client_get_security_conf(
2566     char *      string,
2567     void *      arg)
2568 {
2569     (void)arg;  /* Quiet unused parameter warning */
2570
2571     if(!string || !*string)
2572         return(NULL);
2573
2574     if(strcmp(string, "auth")==0) {
2575         return(getconf_str(CNF_AUTH));
2576     }
2577     if(strcmp(string, "ssh_keys")==0) {
2578         return(getconf_str(CNF_SSH_KEYS));
2579     }
2580     return(NULL);
2581 }