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