2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998, 2000 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: extract_list.c,v 1.117 2006/08/24 01:57:15 paddy_s Exp $
29 * implements the "extract" command in amrecover
34 #include "amrecover.h"
35 #include "fileheader.h"
46 #include "client_util.h"
49 typedef struct EXTRACT_LIST_ITEM {
51 struct EXTRACT_LIST_ITEM *next;
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 */
62 struct EXTRACT_LIST *next;
71 security_stream_t *fd;
72 } amidxtaped_streams[] = {
78 #define NSTREAMS (int)(sizeof(amidxtaped_streams) / sizeof(amidxtaped_streams[0]))
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;
86 static char *amidxtaped_line = NULL;
87 extern char *localhost;
89 /* global pid storage for interrupt handler */
90 pid_t extract_restore_child_pid = -1;
92 static EXTRACT_LIST *extract_list = NULL;
93 static const security_driver_t *amidxtaped_secdrv;
95 unsigned short samba_extract_method = SAMBA_TAR;
97 #define READ_TIMEOUT 240*60
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,
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,
127 static ssize_t read_buffer(int datafd,
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);
139 * Function: ssize_t read_buffer(datafd, buffer, buflen, timeout_s)
142 * read data from input file desciptor waiting up to timeout_s
143 * seconds before returning data.
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.
152 * >0 - Number of data bytes in buffer.
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..
167 SELECT_ARG_TYPE readset;
168 struct timeval timeout;
173 if(datafd < 0 || datafd >= (int)FD_SETSIZE) {
174 errno = EMFILE; /* out of range */
179 spaceleft = (ssize_t)buflen;
183 FD_SET(datafd, &readset);
184 timeout.tv_sec = timeout_s;
186 nfound = select(datafd+1, &readset, NULL, NULL, &timeout);
188 /* Select returned an error. */
189 g_fprintf(stderr,_("select error: %s\n"), strerror(errno));
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"));
206 if(!FD_ISSET(datafd, &readset))
209 /* Select says data is available, so read it. */
210 size = read(datafd, dataptr, (size_t)spaceleft);
212 if ((errno == EINTR) || (errno == EAGAIN)) {
215 if (errno != EPIPE) {
216 g_fprintf(stderr, _("read_buffer: read error - %s"),
224 } while ((size > 0) && (spaceleft > 0));
226 return ((((ssize_t)buflen-spaceleft) > 0) ? ((ssize_t)buflen-spaceleft) : size);
231 first_tape_list(void)
238 /*@keep@*/EXTRACT_LIST *list)
247 EXTRACT_LIST * tape_list)
249 EXTRACT_LIST_ITEM *this, *next;
252 this = tape_list->files;
260 tape_list->files = NULL;
264 /* remove a tape list from the extract list, clearing the tape list
265 beforehand if necessary */
268 EXTRACT_LIST *tape_list)
270 EXTRACT_LIST *this, *prev;
272 if (tape_list == NULL)
275 /* is it first on the list? */
276 if (tape_list == extract_list)
278 extract_list = tape_list->next;
279 clear_tape_list(tape_list);
280 amfree(tape_list->date);
281 amfree(tape_list->tape);
286 /* so not first on list - find it and delete */
288 this = extract_list->next;
291 if (this == tape_list)
293 prev->next = tape_list->next;
294 clear_tape_list(tape_list);
295 amfree(tape_list->date);
296 amfree(tape_list->tape);
307 /* return the number of files on a tape's list */
310 EXTRACT_LIST *tape_list)
312 EXTRACT_LIST_ITEM *fn;
316 for (fn = tape_list->files; fn != NULL; fn = fn->next)
324 clear_extract_list(void)
326 while (extract_list != NULL)
327 delete_tape_list(extract_list);
333 EXTRACT_LIST *tape_list)
335 EXTRACT_LIST_ITEM *fn1, *pfn1, *ofn1;
336 EXTRACT_LIST_ITEM *fn2, *pfn2, *ofn2;
341 fn1 = tape_list->files;
342 while (fn1 != NULL) {
347 while (fn2 != NULL && remove_fn1 == 0) {
349 if(strcmp(fn1->path, fn2->path) == 0) {
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] == '/'))) {
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] == '/'))) {
365 dbprintf(_("removing path %s, it is included in %s\n"),
366 fn2->path, fn1->path);
372 } else if (remove_fn1 == 0) {
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);
386 amfree(tape_list->files);
387 tape_list->files = fn1;
401 clean_extract_list(void)
405 for (this = extract_list; this != NULL; this = this->next)
406 clean_tape_list(this);
410 int add_to_unlink_list(char *path);
411 int do_unlink_list(void);
412 void free_unlink_list(void);
414 typedef struct s_unlink_list {
416 struct s_unlink_list *next;
418 t_unlink_list *unlink_list = NULL;
427 unlink_list = alloc(SIZEOF(*unlink_list));
428 unlink_list->path = stralloc(path);
429 unlink_list->next = NULL;
431 for (ul = unlink_list; ul != NULL; ul = ul->next) {
432 if (strcmp(ul->path, path) == 0)
435 ul = alloc(SIZEOF(*ul));
436 ul->path = stralloc(path);
437 ul->next = unlink_list;
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));
460 free_unlink_list(void)
462 t_unlink_list *ul, *ul1;
464 for (ul = unlink_list; ul != NULL; ul = ul1) {
476 check_file_overwrite(
480 EXTRACT_LIST_ITEM *fn;
481 struct stat stat_buf;
485 for (this = extract_list; this != NULL; this = this->next) {
486 for (fn = this->files; fn != NULL ; fn = fn->next) {
488 /* Check path component of fn->path */
490 path = stralloc2(dir, fn->path);
491 if (path[strlen(path)-1] == '/') {
492 path[strlen(path)-1] = '\0';
495 s = path + strlen(dir) + 1;
496 while((s = strchr(s, '/'))) {
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"),
507 else if (errno != ENOENT) {
508 g_printf(_("Can't stat %s: %s\n"), path, strerror(errno));
517 filename = stralloc2(dir, fn->path);
518 if (filename[strlen(filename)-1] == '/') {
519 filename[strlen(filename)-1] = '\0';
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);
528 } else if(S_ISREG(stat_buf.st_mode)) {
529 g_printf(_("WARNING: Existing file %s will be overwritten\n"),
532 if (add_to_unlink_list(filename)) {
533 g_printf(_("WARNING: Existing entry %s will be deleted\n"),
537 } else if (errno != ENOENT) {
538 g_printf(_("Can't stat %s: %s\n"), filename, strerror(errno));
546 /* returns -1 if error */
547 /* returns 0 on succes */
548 /* returns 1 if already added */
553 EXTRACT_LIST *this, *this1;
554 EXTRACT_LIST_ITEM *that, *curr;
555 char *ditem_path = NULL;
557 ditem_path = stralloc(ditem->path);
558 clean_pathname(ditem_path);
560 for (this = extract_list; this != NULL; this = this->next)
562 /* see if this is the list for the tape */
563 if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
565 /* yes, so add to list */
569 if (strcmp(curr->path,ditem_path) == 0) {
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 */
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);
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)
600 this->next = extract_list;
605 for (this1 = extract_list; this1->next != NULL; this1 = this1->next)
607 /* add in the middle */
608 if(strcmp(this->date,this1->next->date) < 0)
610 this->next = this1->next;
624 /* returns -1 if error */
625 /* returns 0 on deletion */
626 /* returns 1 if not there */
632 EXTRACT_LIST_ITEM *that, *prev;
633 char *ditem_path = NULL;
635 ditem_path = stralloc(ditem->path);
636 clean_pathname(ditem_path);
638 for (this = extract_list; this != NULL; this = this->next)
640 /* see if this is the list for the tape */
641 if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
643 /* yes, so find file on list */
645 if (strcmp(that->path, ditem_path) == 0)
648 this->files = that->next;
651 /* if list empty delete it */
652 if (this->files == NULL)
653 delete_tape_list(this);
661 if (strcmp(that->path, ditem_path) == 0)
663 prev->next = that->next;
689 char *uqglob = unquote_string(glob);
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);
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
704 regex_path = stralloc(regex + 1);
705 regex_path[strlen(regex_path) - 1] = '\0';
706 strappend(regex_path, "[/]*$");
707 add_file(uqglob, regex_path);
719 char *uqregex = unquote_string(regex);
721 if ((s = validate_regexp(uqregex)) != NULL) {
722 g_printf(_("\"%s\" is not a valid regular expression: "), regex);
725 add_file(uqregex, regex);
735 DIR_ITEM *ditem, lditem;
736 char *path_on_disk = NULL;
741 char *dir, *dir_undo, dir_undo_ch = '\0';
742 char *ditem_path = NULL;
743 char *qditem_path = NULL;
746 char *s, *fp, *quoted;
751 if (disk_path == NULL) {
752 g_printf(_("Must select directory before adding files\n"));
755 memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
757 dbprintf(_("add_file: Looking for \"%s\"\n"), regex);
759 if(strcmp(regex, "/[/]*$") == 0) { /* "/" behave like "." */
762 else if(strcmp(regex, "[^/]*[/]*$") == 0) { /* "*" */
763 regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
765 /* remove "/" at end of path */
766 j = (ssize_t)(strlen(regex) - 1);
767 while(j >= 0 && regex[j] == '/')
771 /* convert path (assumed in cwd) to one on disk */
772 if (strcmp(disk_path, "/") == 0) {
774 /* No mods needed if already starts with '/' */
775 path_on_disk = stralloc(regex);
778 path_on_disk = stralloc2("/", regex);
781 char *clean_disk_path = clean_regex(disk_path);
782 path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
783 amfree(clean_disk_path);
786 dbprintf(_("add_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n"),
787 regex, path_on_disk);
791 for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
794 quoted = quote_string(ditem->path);
795 dbprintf(_("add_file: Pondering ditem->path=%s\n"), quoted);
797 if (match(path_on_disk, ditem->path))
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);
807 qditem_path = quote_string(ditem_path);
808 cmd = newstralloc2(cmd, "ORLD ", qditem_path);
810 if(send_command(cmd) == -1) {
813 amfree(path_on_disk);
819 if ((i = get_reply_line()) == -1) {
821 amfree(path_on_disk);
824 if(i==0) { /* assume something wrong */
826 amfree(path_on_disk);
833 lditem.path = newstralloc(lditem.path, ditem->path);
834 /* skip the last line -- duplicate of the preamble */
836 while ((i = get_reply_line()) != 0) {
839 amfree(path_on_disk);
844 if(dir_undo) *dir_undo = dir_undo_ch;
846 cmd = stralloc(l); /* save for error report */
848 continue; /* throw the rest of the lines away */
851 if (!server_happy()) {
857 if(strncmp_const_skip(l, "201-", s, ch) != 0) {
858 err = _("bad reply: not 201-");
863 skip_whitespace(s, ch);
865 err = _("bad reply: missing date field");
869 skip_non_whitespace(s, ch);
871 lditem.date = newstralloc(lditem.date, fp);
874 skip_whitespace(s, ch);
875 if(ch == '\0' || sscanf(s - 1, "%d", &lditem.level) != 1) {
876 err = _("bad reply: cannot parse level field");
881 skip_whitespace(s, ch);
883 err = _("bad reply: missing tape field");
887 skip_non_whitespace(s, ch);
889 lditem.tape = newstralloc(lditem.tape, fp);
892 if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
893 long long fileno_ = (long long)0;
894 skip_whitespace(s, ch);
896 sscanf(s - 1, "%lld", &fileno_) != 1) {
897 err = _("bad reply: cannot parse fileno field");
900 lditem.fileno = (off_t)fileno_;
904 skip_whitespace(s, ch);
906 err = _("bad reply: missing directory field");
910 skip_quoted_string(s, ch);
912 dir_undo_ch = *dir_undo;
915 switch(add_extract_item(&lditem)) {
917 g_printf(_("System error\n"));
918 dbprintf(_("add_file: (Failed) System error\n"));
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);
935 if(!server_happy()) {
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);
949 else /* It is a file */
951 switch(add_extract_item(ditem)) {
953 g_printf(_("System error\n"));
954 dbprintf(_("add_file: (Failed) System error\n"));
958 quoted = quote_string(ditem->path);
959 g_printf(_("Added file %s\n"), quoted);
960 dbprintf(_("add_file: (Successful) Added %s\n"), quoted);
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);
976 amfree(path_on_disk);
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"),
999 char *uqglob = unquote_string(glob);
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);
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
1014 regex_path = stralloc(regex + 1);
1015 regex_path[strlen(regex_path) - 1] = '\0';
1016 strappend(regex_path, "[/]*$");
1017 delete_file(uqglob, regex_path);
1029 char *uqregex = unquote_string(regex);
1031 if ((s = validate_regexp(regex)) != NULL) {
1032 g_printf(_("\"%s\" is not a valid regular expression: "), regex);
1035 delete_file(uqregex, uqregex);
1045 DIR_ITEM *ditem, lditem;
1046 char *path_on_disk = NULL;
1052 char *tape, *tape_undo, tape_undo_ch = '\0';
1053 char *dir_undo, dir_undo_ch = '\0';
1056 char *ditem_path = NULL;
1065 if (disk_path == NULL) {
1066 g_printf(_("Must select directory before deleting files\n"));
1069 memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
1071 dbprintf(_("delete_file: Looking for \"%s\"\n"), path);
1073 if (strcmp(regex, "[^/]*[/]*$") == 0) {
1074 /* Looking for * find everything but single . */
1075 regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
1077 /* remove "/" at end of path */
1078 j = (ssize_t)(strlen(regex) - 1);
1079 while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
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("/\\.[/]*$");
1089 /* No mods needed if already starts with '/' */
1090 path_on_disk = stralloc(regex);
1094 path_on_disk = stralloc2("/", regex);
1097 char *clean_disk_path = clean_regex(disk_path);
1098 path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
1099 amfree(clean_disk_path);
1102 dbprintf(_("delete_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n"),
1103 regex, path_on_disk);
1105 for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
1107 quoted = quote_string(ditem->path);
1108 dbprintf(_("delete_file: Pondering ditem->path=%s\n"), quoted);
1110 if (match(path_on_disk, ditem->path))
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);
1120 qditem_path = quote_string(ditem_path);
1121 cmd = newstralloc2(cmd, "ORLD ", qditem_path);
1122 amfree(qditem_path);
1123 if(send_command(cmd) == -1) {
1126 amfree(path_on_disk);
1131 if ((i = get_reply_line()) == -1) {
1133 amfree(path_on_disk);
1136 if(i==0) /* assume something wrong */
1139 amfree(path_on_disk);
1141 g_printf("%s\n", l);
1145 lditem.path = newstralloc(lditem.path, ditem->path);
1147 tape_undo = dir_undo = NULL;
1148 /* skip the last line -- duplicate of the preamble */
1149 while ((i = get_reply_line()) != 0)
1153 amfree(path_on_disk);
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 */
1163 continue; /* throw the rest of the lines away */
1166 if (!server_happy()) {
1172 if(strncmp_const_skip(l, "201-", s, ch) != 0) {
1173 err = _("bad reply: not 201-");
1178 skip_whitespace(s, ch);
1180 err = _("bad reply: missing date field");
1184 skip_non_whitespace(s, ch);
1187 skip_whitespace(s, ch);
1188 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1189 err = _("bad reply: cannot parse level field");
1192 skip_integer(s, ch);
1194 skip_whitespace(s, ch);
1196 err = _("bad reply: missing tape field");
1200 skip_non_whitespace(s, ch);
1202 tape_undo_ch = *tape_undo;
1205 if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
1206 long long fileno_ = (long long)0;
1207 skip_whitespace(s, ch);
1209 sscanf(s - 1, "%lld", &fileno_) != 1) {
1210 err = _("bad reply: cannot parse fileno field");
1213 fileno = (off_t)fileno_;
1214 skip_integer(s, ch);
1217 skip_whitespace(s, ch);
1219 err = _("bad reply: missing directory field");
1222 skip_non_whitespace(s, ch);
1224 dir_undo_ch = *dir_undo;
1227 lditem.date = newstralloc(lditem.date, date);
1229 lditem.tape = newstralloc(lditem.tape, tape);
1230 switch(delete_extract_item(&lditem)) {
1232 g_printf(_("System error\n"));
1233 dbprintf(_("delete_file: (Failed) System error\n"));
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"),
1245 if(!server_happy()) {
1252 } else if(deleted == 0) {
1253 g_printf(_("Warning - dir '%s' not on tape list\n"),
1255 dbprintf(_("delete_file: dir '%s' not on tape list\n"),
1261 switch(delete_extract_item(ditem)) {
1263 g_printf(_("System error\n"));
1264 dbprintf(_("delete_file: (Failed) System error\n"));
1267 g_printf(_("Deleted %s\n"), ditem->path);
1268 dbprintf(_("delete_file: (Successful) Deleted %s\n"),
1272 g_printf(_("Warning - file '%s' not on tape list\n"),
1274 dbprintf(_("delete_file: file '%s' not on tape list\n"),
1283 amfree(path_on_disk);
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"),
1293 /* print extract list into file. If NULL ptr passed print to screen */
1295 display_extract_list(
1299 EXTRACT_LIST_ITEM *that;
1302 char *pager_command;
1307 if ((pager = getenv("PAGER")) == NULL)
1312 * Set up the pager command so if the pager is terminated, we do
1313 * not get a SIGPIPE back.
1315 pager_command = stralloc2(pager, " ; /bin/cat > /dev/null");
1316 if ((fp = popen(pager_command, "w")) == NULL)
1318 g_printf(_("Warning - can't pipe through %s\n"), pager);
1321 amfree(pager_command);
1325 uqfile = unquote_string(file);
1326 if ((fp = fopen(uqfile, "w")) == NULL)
1328 g_printf(_("Can't open file %s to print extract list into\n"), file);
1335 for (this = extract_list; this != NULL; this = this->next)
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);
1346 g_printf(_("Extract list written to file %s\n"), file);
1357 struct dirent *entry;
1360 if((dir = opendir(fname)) == NULL)
1364 while(!gotentry && (entry = readdir(dir)) != NULL) {
1365 gotentry = !is_dot_or_dotdot(entry->d_name);
1373 /* returns 0 if extract list empty and 1 if it isn't */
1375 is_extract_list_nonempty(void)
1377 return (extract_list != NULL);
1381 /* prints continue prompt and waits for response,
1382 returns 0 if don't, non-0 if do */
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]? ");
1407 prompt = _("Continue [?/Y/n]? ");
1409 fputs(prompt, stdout);
1410 fflush(stdout); fflush(stderr);
1412 if ((line = agets(stdin)) == NULL) {
1422 dbprintf("User prompt: '%s'; response: '%s'\n", prompt, line);
1425 while ((ch = *s++) != '\0' && g_ascii_isspace(ch)) {
1426 (void)ch; /* Quiet empty loop compiler warning */
1430 g_printf(_("Enter a new device name or \"default\"\n"));
1432 g_printf(_("Enter \"y\"es to continue, \"n\"o to stop"));
1434 g_printf(_(", \"s\"kip this tape"));
1437 g_printf(_(" or \"r\"etry this tape"));
1440 g_printf(_(" or \"d\" to change to a new device"));
1444 } else if (get_device) {
1445 char *tmp = stralloc(tape_server_name);
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 */
1452 g_printf(_("No change.\n"));
1458 } else if (ch == '\0' || ch == 'Y' || ch == 'y') {
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') {
1464 } else if (allow_retry && (ch == 'R' || ch == 'r')) {
1466 } else if (allow_skip && (ch == 'S' || ch == 's')) {
1477 send_to_tape_server(
1478 security_stream_t * stream,
1481 char *msg = stralloc2(cmd, "\r\n");
1483 if (security_stream_write(stream, msg, strlen(msg)) < 0)
1485 error(_("Error writing to tape server"));
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. */
1497 extract_files_setup(
1501 char *disk_regex = NULL;
1502 char *host_regex = NULL;
1503 char *clean_datestamp, *ch, *ch1;
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);
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, ";",
1521 protocol_sendreq(tape_server_name, amidxtaped_secdrv,
1522 generic_client_get_security_conf, req, STARTUP_TIMEOUT,
1523 amidxtaped_response, &response_error);
1526 if(response_error != 0) {
1530 disk_regex = alloc(strlen(disk_name) * 2 + 3);
1535 /* we want to force amrestore to only match disk_name exactly */
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 */
1543 /* no break; we do want to fall through... */
1549 /* we want to force amrestore to only match disk_name exactly */
1554 host_regex = alloc(strlen(dump_hostname) * 2 + 3);
1559 /* we want to force amrestore to only match dump_hostname exactly */
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 */
1567 /* no break; we do want to fall through... */
1573 /* we want to force amrestore to only match dump_hostname exactly */
1578 clean_datestamp = stralloc(dump_datestamp);
1579 for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) {
1586 /* push our feature list off to the tape server */
1587 /* XXX assumes that index server and tape server are equivalent, ew */
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);
1596 g_fprintf(stderr, _("amrecover - expecting FEATURES line from amidxtaped\n"));
1600 amfree(clean_datestamp);
1603 am_release_feature_set(tapesrv_features);
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)) {
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);
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);
1622 if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) {
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);
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");
1640 else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) {
1641 /* send to the tape server what tape file we want */
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);
1658 dbprintf(_("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n"),
1659 dump_device_name, host_regex, disk_regex, clean_datestamp);
1664 amfree(clean_datestamp);
1671 * Reads the first block of a tape file.
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"),
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"));
1698 /* bytes_read == buflen */
1699 parse_file_header(buffer, file, (size_t)bytes_read);
1713 extract_files_child(
1715 EXTRACT_LIST * elist)
1718 int extra_params = 0;
1720 char **restore_args = NULL;
1722 EXTRACT_LIST_ITEM *fn;
1723 enum dumptypes dumptype = IS_UNKNOWN;
1724 char buffer[DISK_BLOCK_BYTES];
1728 int passwd_field = -1;
1730 char *domain = NULL, *smbpass = NULL;
1732 backup_support_option_t *bsu;
1733 GPtrArray *errarray;
1735 /* code executed by child to do extraction */
1738 /* make in_fd be our stdin */
1739 if (dup2(in_fd, STDIN_FILENO) == -1)
1741 error(_("dup2 failed in extract_files_child: %s"), strerror(errno));
1745 /* read the file header */
1747 read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO);
1749 if(file.type != F_DUMPFILE) {
1750 print_header(stdout, &file);
1751 error(_("bad header"));
1755 if (file.program != NULL) {
1756 if (strcmp(file.program, "BACKUP") == 0)
1757 dumptype = IS_APPLICATION_API;
1759 if (strcmp(file.program, GNUTAR) == 0)
1760 dumptype = IS_GNUTAR;
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)
1771 if (dumptype == IS_UNKNOWN && strcmp(file.program, SAMBA_CLIENT) ==0) {
1772 if (samba_extract_method == SAMBA_TAR)
1773 dumptype = IS_SAMBA_TAR;
1775 dumptype = IS_SAMBA;
1780 /* form the arguments to restore */
1781 files_off_tape = length_of_tape_list(elist);
1800 #if defined(XFSDUMP)
1801 if (strcmp(file.program, XFSDUMP) == 0) {
1802 extra_params = 4 + files_off_tape;
1810 case IS_APPLICATION_API:
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);
1826 restore_args = (char **)alloc((size_t)((extra_params + files_off_tape + 1)
1831 restore_args[j++] = stralloc("smbclient");
1832 smbpass = findpass(file.disk, &domain);
1834 restore_args[j++] = stralloc(file.disk);
1836 restore_args[j++] = stralloc("-U");
1837 restore_args[j++] = smbpass;
1839 restore_args[j++] = stralloc("-W");
1840 restore_args[j++] = stralloc(domain);
1845 restore_args[j++] = stralloc("-d0");
1846 restore_args[j++] = stralloc("-Tx");
1847 restore_args[j++] = stralloc("-"); /* data on stdin */
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 */
1858 restore_args[j++] = stralloc("tar");
1859 restore_args[j++] = stralloc("-xpvf");
1860 restore_args[j++] = stralloc("-"); /* data on stdin */
1864 restore_args[j++] = stralloc("restore");
1866 restore_args[j++] = stralloc("-xB");
1868 #if defined(XFSDUMP)
1869 if (strcmp(file.program, XFSDUMP) == 0) {
1870 restore_args[j++] = stralloc("-v");
1871 restore_args[j++] = stralloc("silent");
1875 if (strcmp(file.program, VDUMP) == 0) {
1876 restore_args[j++] = stralloc("xf");
1877 restore_args[j++] = stralloc("-"); /* data on stdin */
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 */
1887 case IS_APPLICATION_API:
1889 g_option_t g_options;
1890 g_options.config = get_config_name();
1891 g_options.hostname = dump_hostname;
1893 bsu = backup_support_option(file.application, &g_options,
1894 file.disk, dump_dle->device,
1897 bsu = backup_support_option(file.application, &g_options,
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);
1912 if (bsu->smb_recover_mode && samba_extract_method == SAMBA_SMBCLIENT){
1913 restore_args[j++] = "--recover-mode";
1914 restore_args[j++] = "smb";
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);
1934 for (i = 0, fn = elist->files; i < files_off_tape; i++, fn = fn->next)
1937 case IS_APPLICATION_API:
1942 if (strcmp(fn->path, "/") == 0)
1943 restore_args[j++] = stralloc(".");
1945 restore_args[j++] = stralloc2(".", fn->path);
1949 #if defined(XFSDUMP)
1950 if (strcmp(file.program, XFSDUMP) == 0) {
1952 * xfsrestore needs a -s option before each file to be
1953 * restored, and also wants them to be relative paths.
1955 restore_args[j++] = stralloc("-s");
1956 restore_args[j++] = stralloc(fn->path + 1);
1960 restore_args[j++] = stralloc(fn->path);
1965 #if defined(XFSDUMP)
1966 if (strcmp(file.program, XFSDUMP) == 0) {
1967 restore_args[j++] = stralloc("-");
1968 restore_args[j++] = stralloc(".");
1971 restore_args[j] = NULL;
1976 cmd = stralloc(SAMBA_CLIENT);
1979 /* fall through to ... */
1985 g_fprintf(stderr, _("warning: GNUTAR program not available.\n"));
1986 cmd = stralloc("tar");
1988 cmd = stralloc(GNUTAR);
1995 if (strcmp(file.program, DUMP) == 0) {
1996 cmd = stralloc(RESTORE);
2000 if (strcmp(file.program, VDUMP) == 0) {
2001 cmd = stralloc(VRESTORE);
2005 if (strcmp(file.program, VXDUMP) == 0) {
2006 cmd = stralloc(VXRESTORE);
2009 #if defined(XFSDUMP)
2010 if (strcmp(file.program, XFSDUMP) == 0) {
2011 cmd = stralloc(XFSRESTORE);
2015 g_fprintf(stderr, _("warning: restore program for %s not available.\n"),
2017 cmd = stralloc("restore");
2020 case IS_APPLICATION_API:
2021 cmd = vstralloc(APPLICATION_DIR, "/", file.application, NULL);
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");
2030 dbprintf(_("\t%s\n"), restore_args[i]);
2033 (void)execv(cmd, restore_args);
2034 /* only get here if exec failed */
2036 for (i = 0; i < j; i++) {
2037 amfree(restore_args[i]);
2039 amfree(restore_args);
2041 perror(_("amrecover couldn't exec"));
2042 g_fprintf(stderr, _(" problem executing %s\n"), cmd);
2049 typedef struct ctl_data_s {
2053 EXTRACT_LIST *elist;
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.
2062 writer_intermediary(
2063 EXTRACT_LIST * elist)
2065 ctl_data_t ctl_data;
2066 amwait_t extractor_status;
2068 ctl_data.header_done = 0;
2069 ctl_data.child_pipe[0] = -1;
2070 ctl_data.child_pipe[1] = -1;
2072 ctl_data.elist = elist;
2074 security_stream_read(amidxtaped_streams[DATAFD].fd,
2075 read_amidxtaped_data, &ctl_data);
2077 while(get_amidxtaped_line() >= 0) {
2078 char desired_tape[MAX_TAPE_LABEL_BUF];
2080 /* if prompted for a tape, relay said prompt to the user */
2081 if(sscanf(amidxtaped_line, "FEEDME %132s\n", desired_tape) == 1) {
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),
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);
2095 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "OK");
2098 send_to_tape_server(amidxtaped_streams[CTLFD].fd, "ERROR");
2101 } else if(strncmp_const(amidxtaped_line, "MESSAGE ") == 0) {
2102 g_printf("%s\n",&amidxtaped_line[8]);
2104 g_fprintf(stderr, _("Strange message from tape server: %s"),
2110 /* CTL might be close before DATA */
2112 if (ctl_data.child_pipe[1] != -1)
2113 aclose(ctl_data.child_pipe[1]);
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"));
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);
2131 /* exec restore to do the actual restoration */
2133 /* does the actual extraction of files */
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
2153 EXTRACT_LIST *elist;
2158 tapelist_t *tlist = NULL, *a_tlist;
2159 g_option_t g_options;
2160 GSList *all_level = NULL;
2163 if (!is_extract_list_nonempty())
2165 g_printf(_("Extract list empty - No files to extract!\n"));
2169 clean_extract_list();
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);
2175 if (tape_device_name == NULL) {
2176 if (send_command("TAPE") == -1)
2178 if (get_reply_line() == -1)
2181 if (!server_happy())
2183 g_printf("%s\n", l);
2186 /* skip reply number */
2187 tape_device_name = newstralloc(tape_device_name, l+4);
2190 if (strcmp(tape_device_name, "/dev/null") == 0)
2192 g_printf(_("amrecover: warning: using %s as the tape device will not work\n"),
2197 for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) {
2198 if(elist->tape[0]!='/') {
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:"));
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);
2211 free_tapelist(tlist);
2215 for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) {
2216 if(elist->tape[0]=='/') {
2218 g_printf(_("\nExtracting files from holding disk on host %s.\n"),
2220 g_printf(_("The following files are needed:"));
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);
2229 free_tapelist(tlist);
2234 cwd = g_get_current_dir();
2236 perror(_("extract_list: Current working directory unavailable"));
2240 g_printf(_("Restoring files into directory %s\n"), cwd);
2241 check_file_overwrite(cwd);
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)) {
2252 if (!do_unlink_list()) {
2253 g_fprintf(stderr, _("Can't recover because I can't cleanup the cwd (%s)\n"),
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));
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,
2271 dump_dle->level = NULL;
2274 while ((elist = first_tape_list()) != NULL)
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);
2283 free_tapelist(tlist);
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);
2295 else if (otc == SKIP_TAPE) {
2296 delete_tape_list(elist); /* skip this tape */
2299 dump_device_name = newstralloc(dump_device_name, tape_device_name);
2301 dump_datestamp = newstralloc(dump_datestamp, elist->date);
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,
2310 g_slist_free(dump_dle->level);
2311 dump_dle->level = NULL;
2314 /* connect to the tape handler daemon on the tape drive server */
2315 if ((extract_files_setup(elist->tape, elist->fileno)) == -1)
2317 g_fprintf(stderr, _("amrecover - can't talk to tape server: %s\n"),
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,
2327 last_level = elist->level;
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
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 */
2340 run_client_scripts(EXECUTE_ON_POST_LEVEL_RECOVER, &g_options,
2342 g_slist_free(dump_dle->level);
2343 dump_dle->level = NULL;
2347 dump_dle->level = all_level;
2348 run_client_scripts(EXECUTE_ON_POST_RECOVER, &g_options, dump_dle,
2350 g_slist_free(dump_dle->level);
2352 dump_dle->level = NULL;
2357 amidxtaped_response(
2360 security_handle_t * sech)
2362 int ports[NSTREAMS], *response_error = datap, i;
2367 assert(response_error != NULL);
2368 assert(sech != NULL);
2369 memset(ports, -1, SIZEOF(ports));
2372 errstr = newvstrallocf(errstr, _("[request failed: %s]"), security_geterror(sech));
2373 *response_error = 1;
2376 security_close_connection(sech, dump_hostname);
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);
2383 tok = strtok(pkt->body, " ");
2384 if (tok == NULL || strcmp(tok, "ERROR") != 0)
2387 tok = strtok(NULL, "\n");
2389 errstr = newvstralloc(errstr, "NAK: ", tok, NULL);
2390 *response_error = 1;
2393 errstr = newstralloc(errstr, "request NAK");
2394 *response_error = 2;
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;
2406 #if defined(PACKET_DEBUG)
2407 g_fprintf(stderr, _("got response:\n----\n%s\n----\n\n"), pkt->body);
2410 for(i = 0; i < NSTREAMS; i++) {
2412 amidxtaped_streams[i].fd = NULL;
2416 while((tok = strtok(p, " \n")) != NULL) {
2420 * Error response packets have "ERROR" followed by the error message
2421 * followed by a newline.
2423 if (strcmp(tok, "ERROR") == 0) {
2424 tok = strtok(NULL, "\n");
2426 tok = _("[bogus error packet]");
2427 errstr = newstralloc(errstr, tok);
2428 *response_error = 2;
2434 * Regular packets have CONNECT followed by three streams
2436 if (strcmp(tok, "CONNECT") == 0) {
2439 * Parse the three stream specifiers out of the packet.
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);
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)");
2461 * OPTIONS [options string] '\n'
2463 if (strcmp(tok, "OPTIONS") == 0) {
2464 tok = strtok(NULL, "\n");
2466 extra = stralloc(_("OPTIONS token is missing"));
2470 while((p = strchr(tok, ';')) != NULL) {
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: "),
2489 extra = vstrallocf("next token is \"%s\": expected \"CONNECT\", \"ERROR\" or \"OPTIONS\""),
2490 tok ? tok : _("(null)"));
2496 * Connect the streams to their remote ports
2498 for (i = 0; i < NSTREAMS; i++) {
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));
2512 * Authenticate the streams
2514 for (i = 0; i < NSTREAMS; i++) {
2515 if (amidxtaped_streams[i].fd == NULL)
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));
2527 * The CTLFD and DATAFD streams are mandatory. If we didn't get
2530 if (amidxtaped_streams[CTLFD].fd == NULL) {
2531 errstr = newvstrallocf(errstr, _("[couldn't open CTL streams]"));
2534 if (amidxtaped_streams[DATAFD].fd == NULL) {
2535 errstr = newvstrallocf(errstr, _("[couldn't open DATA streams]"));
2539 /* everything worked */
2540 *response_error = 0;
2545 errstr = newvstrallocf(errstr,
2546 _("[parse of reply message failed: %s]"), extra);
2548 errstr = newvstrallocf(errstr,
2549 _("[parse of reply message failed: (no additional information)"));
2552 *response_error = 2;
2557 *response_error = 1;
2561 * This is called when everything needs to shut down so event_loop()
2565 stop_amidxtaped(void)
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;
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 */
2583 get_amidxtaped_line(void)
2589 amfree(amidxtaped_line);
2591 ctl_buffer = stralloc("");
2593 while (!strstr(ctl_buffer,"\r\n")) {
2594 size = security_stream_read_sync(amidxtaped_streams[CTLFD].fd, &buf);
2598 else if(size == 0) {
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';
2606 ctl_buffer = newbuf;
2609 s = strstr(ctl_buffer,"\r\n");
2611 newbuf = stralloc(s+2);
2612 amidxtaped_line = stralloc(ctl_buffer);
2614 ctl_buffer = newbuf;
2620 read_amidxtaped_data(
2625 ctl_data_t *ctl_data = (ctl_data_t *)cookie;
2626 assert(cookie != NULL);
2629 errstr = newstralloc2(errstr, _("amidxtaped read: "),
2630 security_stream_geterror(amidxtaped_streams[DATAFD].fd));
2635 * EOF. Stop and return.
2638 security_stream_close(amidxtaped_streams[DATAFD].fd);
2639 amidxtaped_streams[DATAFD].fd = NULL;
2641 * If the mesg fd has also shut down, then we're done.
2646 assert(buf != NULL);
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"),
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);
2665 if (ctl_data->pid == -1) {
2666 errstr = newstralloc(errstr, _("writer_intermediary - error forking child"));
2667 g_printf(_("writer_intermediary - error forking child"));
2670 aclose(ctl_data->child_pipe[0]);
2673 * We ignore errors while writing to the index file.
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);
2680 amidxtaped_client_get_security_conf(
2684 (void)arg; /* Quiet unused parameter warning */
2686 if(!string || !*string)
2689 if(strcmp(string, "auth")==0) {
2690 return(getconf_str(CNF_AUTH));
2692 if(strcmp(string, "ssh_keys")==0) {
2693 return(getconf_str(CNF_SSH_KEYS));