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.
29 * implements the "extract" command in amrecover
35 #include "amrecover.h"
36 #include "fileheader.h"
45 typedef struct EXTRACT_LIST_ITEM {
48 struct EXTRACT_LIST_ITEM *next;
52 typedef struct EXTRACT_LIST {
53 char *date; /* date tape created */
54 int level; /* level of dump */
55 char *tape; /* tape label */
56 off_t fileno; /* fileno on tape */
57 EXTRACT_LIST_ITEM *files; /* files to get off tape */
59 struct EXTRACT_LIST *next;
66 char *dump_device_name = NULL;
68 extern char *localhost;
70 /* global pid storage for interrupt handler */
71 pid_t extract_restore_child_pid = -1;
73 static EXTRACT_LIST *extract_list = NULL;
74 static int tape_control_sock = -1;
75 static int tape_data_sock = -1;
78 unsigned short samba_extract_method = SAMBA_TAR;
79 #endif /* SAMBA_CLIENT */
81 #define READ_TIMEOUT 240*60
83 EXTRACT_LIST *first_tape_list(void);
84 EXTRACT_LIST *next_tape_list(EXTRACT_LIST *list);
85 int is_extract_list_nonempty(void);
86 int length_of_tape_list(EXTRACT_LIST *tape_list);
87 void add_file(char *path, char *regex);
88 void add_glob(char *glob);
89 void add_regex(char *regex);
90 void clear_extract_list(void);
91 void clean_tape_list(EXTRACT_LIST *tape_list);
92 void clean_extract_list(void);
93 void delete_file(char *path, char *regex);
94 void delete_glob(char *glob);
95 void delete_regex(char *regex);
96 void delete_tape_list(EXTRACT_LIST *tape_list);
97 void display_extract_list(char *file);
98 void extract_files(void);
99 void read_file_header(char *buffer,
103 void writer_intermediary(int ctl_fd, int data_fd, EXTRACT_LIST *elist);
104 void writer_intermediary(int ctl_fd, int data_fd, EXTRACT_LIST *elist);
106 static int add_extract_item(DIR_ITEM *ditem);
107 static int delete_extract_item(DIR_ITEM *ditem);
108 static int extract_files_setup(char *label, off_t fsf);
109 static int okay_to_continue(int allow_tape,
112 static int okay_to_continue(int, int, int);
113 static ssize_t read_buffer(int datafd,
117 static void clear_tape_list(EXTRACT_LIST *tape_list);
118 static void extract_files_child(int in_fd, EXTRACT_LIST *elist);
119 static void send_to_tape_server(int tss, char *cmd);
123 * Function: ssize_t read_buffer(datafd, buffer, buflen, timeout_s)
126 * read data from input file desciptor waiting up to timeout_s
127 * seconds before returning data.
130 * datafd - File descriptor to read from.
131 * buffer - Buffer to read into.
132 * buflen - Maximum number of bytes to read into buffer.
133 * timeout_s - Seconds to wait before returning what was already read.
136 * >0 - Number of data bytes in buffer.
138 * -1 - errno == ETIMEDOUT if no data available in specified time.
139 * errno == ENFILE if datafd is invalid.
140 * otherwise errno is set by select or read..
152 struct timeval timeout;
157 if(datafd < 0 || datafd >= (int)FD_SETSIZE) {
158 errno = EMFILE; /* out of range */
163 spaceleft = (ssize_t)buflen;
167 FD_SET(datafd, &readset);
168 timeout.tv_sec = timeout_s;
170 nfound = select(datafd+1, &readset, NULL, NULL, &timeout);
172 /* Select returned an error. */
173 g_fprintf(stderr,_("select error: %s\n"), strerror(errno));
179 /* Select timed out. */
180 if (timeout_s != 0) {
181 /* Not polling: a real read timeout */
182 g_fprintf(stderr,_("timeout waiting for restore\n"));
183 g_fprintf(stderr,_("increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n"));
190 if(!FD_ISSET(datafd, &readset))
193 /* Select says data is available, so read it. */
194 size = read(datafd, dataptr, (size_t)spaceleft);
196 if ((errno == EINTR) || (errno == EAGAIN)) {
199 if (errno != EPIPE) {
200 g_fprintf(stderr, _("read_buffer: read error - %s"),
208 } while ((size > 0) && (spaceleft > 0));
210 return ((((ssize_t)buflen-spaceleft) > 0) ? ((ssize_t)buflen-spaceleft) : size);
215 first_tape_list(void)
222 /*@keep@*/ EXTRACT_LIST *list)
231 EXTRACT_LIST * tape_list)
233 EXTRACT_LIST_ITEM *this, *next;
236 this = tape_list->files;
244 tape_list->files = NULL;
248 /* remove a tape list from the extract list, clearing the tape list
249 beforehand if necessary */
252 EXTRACT_LIST * tape_list)
254 EXTRACT_LIST *this, *prev;
256 if (tape_list == NULL)
259 /* is it first on the list? */
260 if (tape_list == extract_list)
262 extract_list = tape_list->next;
263 clear_tape_list(tape_list);
264 amfree(tape_list->date);
265 amfree(tape_list->tape);
270 /* so not first on list - find it and delete */
272 this = extract_list->next;
275 if (this == tape_list)
277 prev->next = tape_list->next;
278 clear_tape_list(tape_list);
279 amfree(tape_list->date);
280 amfree(tape_list->tape);
291 /* return the number of files on a tape's list */
294 EXTRACT_LIST * tape_list)
296 EXTRACT_LIST_ITEM *fn;
300 for (fn = tape_list->files; fn != NULL; fn = fn->next)
308 clear_extract_list(void)
310 while (extract_list != NULL)
311 delete_tape_list(extract_list);
317 EXTRACT_LIST *tape_list)
319 EXTRACT_LIST_ITEM *fn1, *pfn1, *ofn1;
320 EXTRACT_LIST_ITEM *fn2, *pfn2, *ofn2;
325 fn1 = tape_list->files;
326 while (fn1 != NULL) {
331 while (fn2 != NULL && remove_fn1 == 0) {
333 if(strcmp(fn1->path, fn2->path) == 0) {
335 } else if (strncmp(fn1->path, fn2->path, strlen(fn1->path)) == 0 &&
336 ((strlen(fn2->path) > strlen(fn1->path) &&
337 fn2->path[strlen(fn1->path)] == '/') ||
338 (fn1->path[strlen(fn1->path)-1] == '/'))) {
340 } else if (strncmp(fn2->path, fn1->path, strlen(fn2->path)) == 0 &&
341 ((strlen(fn1->path) > strlen(fn2->path) &&
342 fn1->path[strlen(fn2->path)] == '/') ||
343 (fn2->path[strlen(fn2->path)-1] == '/'))) {
349 dbprintf(_("removing path %s, it is included in %s\n"),
350 fn2->path, fn1->path);
356 } else if (remove_fn1 == 0) {
362 if(remove_fn1 != 0) {
363 /* fn2->path is always valid */
364 /*@i@*/ dbprintf(_("removing path %s, it is included in %s\n"),
365 /*@i@*/ fn1->path, fn2->path);
370 amfree(tape_list->files);
371 tape_list->files = fn1;
385 clean_extract_list(void)
389 for (this = extract_list; this != NULL; this = this->next)
390 clean_tape_list(this);
394 /* returns -1 if error */
395 /* returns 0 on succes */
396 /* returns 1 if already added */
401 EXTRACT_LIST *this, *this1;
402 EXTRACT_LIST_ITEM *that, *curr;
403 char *ditem_path = NULL;
405 ditem_path = stralloc(ditem->path);
406 clean_pathname(ditem_path);
408 for (this = extract_list; this != NULL; this = this->next)
410 /* see if this is the list for the tape */
411 if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
413 /* yes, so add to list */
417 if (strcmp(curr->path, ditem_path) == 0) {
423 that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
424 that->path = stralloc(ditem_path);
425 that->next = this->files;
426 this->files = that; /* add at front since easiest */
432 /* so this is the first time we have seen this tape */
433 this = (EXTRACT_LIST *)alloc(sizeof(EXTRACT_LIST));
434 this->tape = stralloc(ditem->tape);
435 this->level = ditem->level;
436 this->fileno = ditem->fileno;
437 this->date = stralloc(ditem->date);
438 that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
439 that->path = stralloc(ditem_path);
443 /* add this in date increasing order */
444 /* because restore must be done in this order */
445 /* add at begining */
446 if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0)
448 this->next = extract_list;
453 for (this1 = extract_list; this1->next != NULL; this1 = this1->next)
455 /* add in the middle */
456 if(strcmp(this->date,this1->next->date) < 0)
458 this->next = this1->next;
472 /* returns -1 if error */
473 /* returns 0 on deletion */
474 /* returns 1 if not there */
480 EXTRACT_LIST_ITEM *that, *prev;
481 char *ditem_path = NULL;
483 ditem_path = stralloc(ditem->path);
484 clean_pathname(ditem_path);
486 for (this = extract_list; this != NULL; this = this->next)
488 /* see if this is the list for the tape */
489 if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
491 /* yes, so find file on list */
493 if (strcmp(that->path, ditem_path) == 0)
496 this->files = that->next;
499 /* if list empty delete it */
500 if (this->files == NULL)
501 delete_tape_list(this);
509 if (strcmp(that->path, ditem_path) == 0)
511 prev->next = that->next;
536 char *uqglob = unquote_string(glob);
538 regex = glob_to_regex(uqglob);
539 dbprintf(_("add_glob (%s) -> %s\n"), uqglob, regex);
540 if ((s = validate_regexp(regex)) != NULL) {
541 g_printf(_("%s is not a valid shell wildcard pattern: "), glob);
545 * glob_to_regex() anchors the beginning of the pattern with ^,
546 * but we will be tacking it onto the end of the current directory
547 * in add_file, so strip that off. Also, it anchors the end with
548 * $, but we need to match an optional trailing /, so tack that on
551 regex_path = stralloc(regex + 1);
552 regex_path[strlen(regex_path) - 1] = '\0';
553 strappend(regex_path, "[/]*$");
554 add_file(uqglob, regex_path);
566 char *uqregex = unquote_string(regex);
568 if ((s = validate_regexp(uqregex)) != NULL) {
569 g_printf(_("%s is not a valid regular expression: "), regex);
572 add_file(uqregex, regex);
581 DIR_ITEM *ditem, lditem;
582 char *path_on_disk = NULL;
583 char *path_on_disk_slash = NULL;
588 char *dir_undo, dir_undo_ch = '\0';
589 char *ditem_path = NULL;
592 char *s, *fp, *quoted;
597 if (disk_path == NULL) {
598 g_printf(_("Must select directory before adding files\n"));
601 memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
603 dbprintf(_("add_file: Looking for \"%s\"\n"), regex);
605 if(strcmp(regex, "/[/]*$") == 0) { /* "/" behave like "." */
608 else if(strcmp(regex, "[^/]*[/]*$") == 0) { /* "*" */
609 regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
611 /* remove "/" at end of path */
612 j = (ssize_t)(strlen(regex) - 1);
613 while(j >= 0 && regex[j] == '/')
617 /* convert path (assumed in cwd) to one on disk */
618 if (strcmp(disk_path, "/") == 0) {
620 /* No mods needed if already starts with '/' */
621 path_on_disk = stralloc(regex);
624 path_on_disk = stralloc2("/", regex);
627 char *clean_disk_path = clean_regex(disk_path, 0);
628 path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
629 amfree(clean_disk_path);
632 path_on_disk_slash = stralloc2(path_on_disk, "/");
634 dbprintf(_("add_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n"),
635 regex, path_on_disk);
639 for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
642 quoted = quote_string(ditem->path);
643 dbprintf(_("add_file: Pondering ditem->path=%s\n"), quoted);
645 if (match(path_on_disk, ditem->path)
646 || match(path_on_disk_slash, ditem->path))
649 j = (ssize_t)strlen(ditem->path);
650 if((j > 0 && ditem->path[j-1] == '/')
651 || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
652 { /* It is a directory */
653 ditem_path = newstralloc(ditem_path, ditem->path);
654 clean_pathname(ditem_path);
656 cmd = newstralloc2(cmd, "ORLD ", ditem_path);
657 if(send_command(cmd) == -1) {
660 amfree(path_on_disk);
661 amfree(path_on_disk_slash);
667 if ((i = get_reply_line()) == -1) {
669 amfree(path_on_disk);
670 amfree(path_on_disk_slash);
673 if(i==0) { /* assume something wrong */
675 amfree(path_on_disk);
676 amfree(path_on_disk_slash);
678 g_printf(_("%s\n"), l);
683 lditem.path = newstralloc(lditem.path, ditem->path);
684 /* skip the last line -- duplicate of the preamble */
686 while ((i = get_reply_line()) != 0) {
689 amfree(path_on_disk);
690 amfree(path_on_disk_slash);
695 if(dir_undo) *dir_undo = dir_undo_ch;
697 cmd = stralloc(l); /* save for error report */
699 continue; /* throw the rest of the lines away */
702 if (!server_happy()) {
708 if(strncmp_const_skip(l, "201-", s, ch) != 0) {
709 err = _("bad reply: not 201-");
714 skip_whitespace(s, ch);
716 err = _("bad reply: missing date field");
720 skip_non_whitespace(s, ch);
722 lditem.date = newstralloc(lditem.date, fp);
725 skip_whitespace(s, ch);
726 if(ch == '\0' || sscanf(s - 1, "%d", &lditem.level) != 1) {
727 err = _("bad reply: cannot parse level field");
732 skip_whitespace(s, ch);
734 err = _("bad reply: missing tape field");
738 skip_non_whitespace(s, ch);
740 lditem.tape = newstralloc(lditem.tape, fp);
743 if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
744 long long fileno_ = (long long)0;
745 skip_whitespace(s, ch);
747 sscanf(s - 1, "%lld", &fileno_) != 1) {
748 err = _("bad reply: cannot parse fileno field");
751 lditem.fileno = (off_t)fileno_;
755 skip_whitespace(s, ch);
757 err = _("bad reply: missing directory field");
760 skip_quoted_string(s, ch);
762 dir_undo_ch = *dir_undo;
765 switch(add_extract_item(&lditem)) {
767 g_printf(_("System error\n"));
768 dbprintf(_("add_file: (Failed) System error\n"));
772 quoted = quote_string(lditem.path);
773 g_printf(_("Added dir %s at date %s\n"),
774 quoted, lditem.date);
775 dbprintf(_("add_file: (Successful) Added dir %s at date %s\n"),
776 quoted, lditem.date);
785 if(!server_happy()) {
792 } else if(added == 0) {
793 quoted = quote_string(ditem_path);
794 g_printf(_("dir %s already added\n"), quoted);
795 dbprintf(_("add_file: dir %s already added\n"), quoted);
799 else /* It is a file */
801 switch(add_extract_item(ditem)) {
803 g_printf(_("System error\n"));
804 dbprintf(_("add_file: (Failed) System error\n"));
808 quoted = quote_string(ditem->path);
809 g_printf(_("Added file %s\n"), quoted);
810 dbprintf(_("add_file: (Successful) Added %s\n"), quoted);
815 quoted = quote_string(ditem->path);
816 g_printf(_("File %s already added\n"), quoted);
817 dbprintf(_("add_file: file %s already added\n"), quoted);
827 amfree(path_on_disk);
828 amfree(path_on_disk_slash);
831 quoted = quote_string(path);
832 g_printf(_("File %s doesn't exist in directory\n"), quoted);
833 dbprintf(_("add_file: (Failed) File %s doesn't exist in directory\n"),
847 char *uqglob = unquote_string(glob);
849 regex = glob_to_regex(uqglob);
850 dbprintf(_("delete_glob (%s) -> %s\n"), uqglob, regex);
851 if ((s = validate_regexp(regex)) != NULL) {
852 g_printf(_("\"%s\" is not a valid shell wildcard pattern: "), glob);
856 * glob_to_regex() anchors the beginning of the pattern with ^,
857 * but we will be tacking it onto the end of the current directory
858 * in add_file, so strip that off. Also, it anchors the end with
859 * $, but we need to match an optional trailing /, so tack that on
862 regex_path = stralloc(regex + 1);
863 regex_path[strlen(regex_path) - 1] = '\0';
864 strappend(regex_path, "[/]*$");
865 delete_file(uqglob, regex_path);
877 char *uqregex = unquote_string(regex);
879 if ((s = validate_regexp(regex)) != NULL) {
880 g_printf(_("\"%s\" is not a valid regular expression: "), regex);
883 delete_file(uqregex, uqregex);
893 DIR_ITEM *ditem, lditem;
894 char *path_on_disk = NULL;
895 char *path_on_disk_slash = NULL;
901 char *tape, *tape_undo, tape_undo_ch = '\0';
902 char *dir_undo, dir_undo_ch = '\0';
904 char *ditem_path = NULL;
912 if (disk_path == NULL) {
913 g_printf(_("Must select directory before deleting files\n"));
916 memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
918 dbprintf(_("delete_file: Looking for \"%s\"\n"), path);
920 if (strcmp(regex, "[^/]*[/]*$") == 0) {
921 /* Looking for * find everything but single . */
922 regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
924 /* remove "/" at end of path */
925 j = (ssize_t)(strlen(regex) - 1);
926 while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
929 /* convert path (assumed in cwd) to one on disk */
930 if (strcmp(disk_path, "/") == 0) {
932 if (strcmp(regex, "/[/]*$") == 0) {
933 /* We want "/" to match the directory itself: "/." */
934 path_on_disk = stralloc("/\\.[/]*$");
936 /* No mods needed if already starts with '/' */
937 path_on_disk = stralloc(regex);
941 path_on_disk = stralloc2("/", regex);
944 char *clean_disk_path = clean_regex(disk_path, 0);
945 path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
946 amfree(clean_disk_path);
949 path_on_disk_slash = stralloc2(path_on_disk, "/");
951 dbprintf(_("delete_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n"),
952 regex, path_on_disk);
954 for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
956 quoted = quote_string(ditem->path);
957 dbprintf(_("delete_file: Pondering ditem->path=%s\n"), quoted);
959 if (match(path_on_disk, ditem->path)
960 || match(path_on_disk_slash, ditem->path))
963 j = (ssize_t)strlen(ditem->path);
964 if((j > 0 && ditem->path[j-1] == '/')
965 || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
966 { /* It is a directory */
967 ditem_path = newstralloc(ditem_path, ditem->path);
968 clean_pathname(ditem_path);
970 cmd = newstralloc2(cmd, "ORLD ", ditem_path);
971 if(send_command(cmd) == -1) {
974 amfree(path_on_disk);
975 amfree(path_on_disk_slash);
980 if ((i = get_reply_line()) == -1) {
982 amfree(path_on_disk);
983 amfree(path_on_disk_slash);
986 if(i==0) /* assume something wrong */
989 amfree(path_on_disk);
990 amfree(path_on_disk_slash);
996 lditem.path = newstralloc(lditem.path, ditem->path);
998 tape_undo = dir_undo = NULL;
999 /* skip the last line -- duplicate of the preamble */
1000 while ((i = get_reply_line()) != 0)
1004 amfree(path_on_disk);
1005 amfree(path_on_disk_slash);
1010 if(tape_undo) *tape_undo = tape_undo_ch;
1011 if(dir_undo) *dir_undo = dir_undo_ch;
1012 tape_undo = dir_undo = NULL;
1013 cmd = stralloc(l); /* save for the error report */
1015 continue; /* throw the rest of the lines away */
1018 if (!server_happy()) {
1024 if(strncmp_const_skip(l, "201-", s, ch) != 0) {
1025 err = _("bad reply: not 201-");
1030 skip_whitespace(s, ch);
1032 err = _("bad reply: missing date field");
1036 skip_non_whitespace(s, ch);
1039 skip_whitespace(s, ch);
1040 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1041 err = _("bad reply: cannot parse level field");
1044 skip_integer(s, ch);
1046 skip_whitespace(s, ch);
1048 err = _("bad reply: missing tape field");
1052 skip_non_whitespace(s, ch);
1054 tape_undo_ch = *tape_undo;
1057 if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
1058 long long fileno_ = (long long)0;
1059 skip_whitespace(s, ch);
1061 sscanf(s - 1, "%lld", &fileno_) != 1) {
1062 err = _("bad reply: cannot parse fileno field");
1065 skip_integer(s, ch);
1068 skip_whitespace(s, ch);
1070 err = _("bad reply: missing directory field");
1073 skip_non_whitespace(s, ch);
1075 dir_undo_ch = *dir_undo;
1078 lditem.date = newstralloc(lditem.date, date);
1080 lditem.tape = newstralloc(lditem.tape, tape);
1081 switch(delete_extract_item(&lditem)) {
1083 g_printf(_("System error\n"));
1084 dbprintf(_("delete_file: (Failed) System error\n"));
1087 g_printf(_("Deleted dir %s at date %s\n"), ditem_path, date);
1088 dbprintf(_("delete_file: (Successful) Deleted dir %s at date %s\n"),
1096 if(!server_happy()) {
1103 } else if(deleted == 0) {
1104 g_printf(_("Warning - dir '%s' not on tape list\n"),
1106 dbprintf(_("delete_file: dir '%s' not on tape list\n"),
1112 switch(delete_extract_item(ditem)) {
1114 g_printf(_("System error\n"));
1115 dbprintf(_("delete_file: (Failed) System error\n"));
1118 g_printf(_("Deleted %s\n"), ditem->path);
1119 dbprintf(_("delete_file: (Successful) Deleted %s\n"),
1123 g_printf(_("Warning - file '%s' not on tape list\n"),
1125 dbprintf(_("delete_file: file '%s' not on tape list\n"),
1134 amfree(path_on_disk);
1135 amfree(path_on_disk_slash);
1138 g_printf(_("File %s doesn't exist in directory\n"), path);
1139 dbprintf(_("delete_file: (Failed) File %s doesn't exist in directory\n"),
1145 /* print extract list into file. If NULL ptr passed print to screen */
1147 display_extract_list(
1151 EXTRACT_LIST_ITEM *that;
1154 char *pager_command;
1159 if ((pager = getenv("PAGER")) == NULL)
1164 * Set up the pager command so if the pager is terminated, we do
1165 * not get a SIGPIPE back.
1167 pager_command = stralloc2(pager, " ; /bin/cat > /dev/null");
1168 if ((fp = popen(pager_command, "w")) == NULL)
1170 g_printf(_("Warning - can't pipe through %s\n"), pager);
1173 amfree(pager_command);
1177 uqfile = unquote_string(file);
1178 if ((fp = fopen(uqfile, "w")) == NULL)
1180 g_printf(_("Can't open file %s to print extract list into\n"), file);
1187 for (this = extract_list; this != NULL; this = this->next)
1189 g_fprintf(fp, _("TAPE %s LEVEL %d DATE %s\n"),
1190 this->tape, this->level, this->date);
1191 for (that = this->files; that != NULL; that = that->next)
1192 g_fprintf(fp, "\t%s\n", that->path);
1198 g_printf(_("Extract list written to file %s\n"), file);
1204 /* returns 0 if extract list empty and 1 if it isn't */
1206 is_extract_list_nonempty(void)
1208 return (extract_list != NULL);
1212 /* prints continue prompt and waits for response,
1213 returns 0 if don't, non-0 if do */
1230 prompt = _("New tape device [?]: ");
1231 } else if (allow_tape && allow_skip) {
1232 prompt = _("Continue [?/Y/n/s/t]? ");
1233 } else if (allow_tape && !allow_skip) {
1234 prompt = _("Continue [?/Y/n/t]? ");
1235 } else if (allow_retry) {
1236 prompt = _("Continue [?/Y/n/r]? ");
1238 prompt = _("Continue [?/Y/n]? ");
1240 fputs(prompt, stdout);
1241 fflush(stdout); fflush(stderr);
1243 if ((line = agets(stdin)) == NULL) {
1254 while ((ch = *s++) != '\0' && g_ascii_isspace(ch)) {
1255 (void)ch; /* Quiet empty loop body warning */
1259 g_printf(_("Enter a new device ([host:]device) or \"default\"\n"));
1261 g_printf(_("Enter \"y\"es to continue, \"n\"o to stop"));
1263 g_printf(_(", \"s\"kip this tape"));
1266 g_printf(_(" or \"r\"etry this tape"));
1269 g_printf(_(" or \"t\"ape to change tape drives"));
1273 } else if (get_tape) {
1276 } else if (ch == '\0' || ch == 'Y' || ch == 'y') {
1278 } else if (allow_tape && (ch == 'T' || ch == 't')) {
1280 } else if (ch == 'N' || ch == 'n') {
1282 } else if (allow_retry && (ch == 'R' || ch == 'r')) {
1284 } else if (allow_skip && (ch == 'S' || ch == 's')) {
1295 send_to_tape_server(
1299 char *msg = stralloc2(cmd, "\r\n");
1301 if (full_write(tss, msg, strlen(msg)) < strlen(msg))
1303 error(_("Error writing to tape server: %s"), strerror(errno));
1310 /* start up connection to tape server and set commands to initiate
1311 transfer of dump image.
1312 Return tape server socket on success, -1 on error. */
1314 extract_files_setup(
1319 in_port_t my_port, my_data_port;
1320 char *disk_regex = NULL;
1321 char *host_regex = NULL;
1322 char *service_name = NULL;
1324 char *clean_datestamp, *ch, *ch1;
1325 char *our_feature_string = NULL;
1328 service_name = stralloc2("amidxtape", SERVICE_SUFFIX);
1330 /* get tape server details */
1331 if ((sp = getservbyname(service_name, "tcp")) == NULL)
1333 g_printf(_("%s/tcp unknown protocol - config error?\n"), service_name);
1334 amfree(service_name);
1337 amfree(service_name);
1338 seteuid(0); /* it either works ... */
1340 tape_control_sock = stream_client_privileged(tape_server_name,
1341 (in_port_t)ntohs((in_port_t)sp->s_port),
1346 if (tape_control_sock < 0)
1348 g_printf(_("cannot connect to %s: %s\n"), tape_server_name, strerror(errno));
1351 if (my_port >= IPPORT_RESERVED) {
1352 aclose(tape_control_sock);
1353 g_printf(_("did not get a reserved port: %u\n"), (unsigned)my_port);
1358 seteuid(getuid()); /* put it back */
1360 /* do the security thing */
1361 line = get_security();
1362 send_to_tape_server(tape_control_sock, line);
1363 memset(line, '\0', strlen(line));
1366 disk_regex = alloc(strlen(disk_name) * 2 + 3);
1371 /* we want to force amrestore to only match disk_name exactly */
1374 /* We need to escape some characters first... NT compatibilty crap */
1375 for (; *ch != 0; ch++, ch1++) {
1376 switch (*ch) { /* done this way in case there are more */
1379 /* no break; we do want to fall through... */
1385 /* we want to force amrestore to only match disk_name exactly */
1390 host_regex = alloc(strlen(dump_hostname) * 2 + 3);
1395 /* we want to force amrestore to only match dump_hostname exactly */
1398 /* We need to escape some characters first... NT compatibilty crap */
1399 for (; *ch != 0; ch++, ch1++) {
1400 switch (*ch) { /* done this way in case there are more */
1403 /* no break; we do want to fall through... */
1409 /* we want to force amrestore to only match dump_hostname exactly */
1414 clean_datestamp = stralloc(dump_datestamp);
1415 for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) {
1423 /* push our feature list off to the tape server */
1424 /* XXX assumes that index server and tape server are equivalent, ew */
1425 if(am_has_feature(indexsrv_features, fe_amidxtaped_exchange_features)){
1426 char buffer[32768] = "\0";
1428 our_feature_string = am_feature_to_string(our_features);
1429 tt = newstralloc2(tt, "FEATURES=", our_feature_string);
1430 send_to_tape_server(tape_control_sock, tt);
1431 if (read(tape_control_sock, buffer, sizeof(buffer)) <= 0) {
1432 error(_("Could not read features from control socket\n"));
1435 tapesrv_features = am_string_to_feature(buffer);
1436 amfree(our_feature_string);
1440 if(am_has_feature(indexsrv_features, fe_amidxtaped_header) &&
1441 am_has_feature(indexsrv_features, fe_amidxtaped_device) &&
1442 am_has_feature(indexsrv_features, fe_amidxtaped_host) &&
1443 am_has_feature(indexsrv_features, fe_amidxtaped_disk) &&
1444 am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) {
1446 if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) {
1447 tt = newstralloc2(tt, "CONFIG=", config);
1448 send_to_tape_server(tape_control_sock, tt);
1450 if(am_has_feature(indexsrv_features, fe_amidxtaped_label) &&
1451 label && label[0] != '/') {
1452 tt = newstralloc2(tt,"LABEL=",label);
1453 send_to_tape_server(tape_control_sock, tt);
1455 if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) {
1457 g_snprintf(v_fsf, 99, "%lld", (long long)fsf);
1458 tt = newstralloc2(tt, "FSF=",v_fsf);
1459 send_to_tape_server(tape_control_sock, tt);
1461 send_to_tape_server(tape_control_sock, "HEADER");
1462 tt = newstralloc2(tt, "DEVICE=", dump_device_name);
1463 send_to_tape_server(tape_control_sock, tt);
1464 tt = newstralloc2(tt, "HOST=", host_regex);
1465 send_to_tape_server(tape_control_sock, tt);
1466 tt = newstralloc2(tt, "DISK=", disk_regex);
1467 send_to_tape_server(tape_control_sock, tt);
1468 tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp);
1469 send_to_tape_server(tape_control_sock, tt);
1470 send_to_tape_server(tape_control_sock, "END");
1473 else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) {
1474 /* send to the tape server what tape file we want */
1483 send_to_tape_server(tape_control_sock, "6");
1484 send_to_tape_server(tape_control_sock, "-h");
1485 send_to_tape_server(tape_control_sock, "-p");
1486 send_to_tape_server(tape_control_sock, dump_device_name);
1487 send_to_tape_server(tape_control_sock, host_regex);
1488 send_to_tape_server(tape_control_sock, disk_regex);
1489 send_to_tape_server(tape_control_sock, clean_datestamp);
1491 dbprintf(_("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n"),
1492 dump_device_name, host_regex, disk_regex, clean_datestamp);
1496 * split-restoring amidxtaped versions will expect to set up a data
1497 * connection for dumpfile data, distinct from the socket we're already
1498 * using for control data
1501 if(am_has_feature(tapesrv_features, fe_recover_splits)){
1503 in_port_t data_port = (in_port_t)-1;
1506 nread = read(tape_control_sock, buffer, sizeof(buffer));
1509 error(_("Could not read from control socket: %s\n"),
1514 buffer[nread] = '\0';
1515 if (sscanf(buffer, "CONNECT %hu\n",
1516 (unsigned short *)&data_port) != 1) {
1517 error(_("Recieved invalid port number message from control socket: %s\n"),
1522 tape_data_sock = stream_client_privileged(server_name,
1528 if(tape_data_sock == -1){
1529 error(_("Unable to make data connection to server: %s\n"),
1534 amfree(our_feature_string);
1536 line = get_security();
1538 send_to_tape_server(tape_data_sock, line);
1539 memset(line, '\0', strlen(line));
1545 amfree(clean_datestamp);
1547 return tape_control_sock;
1552 * Reads the first block of a tape file.
1564 bytes_read = read_buffer(tapedev, buffer, buflen, READ_TIMEOUT);
1565 if(bytes_read < 0) {
1566 error(_("error reading header (%s), check amidxtaped.*.debug on server"),
1571 if((size_t)bytes_read < buflen) {
1572 g_fprintf(stderr, plural(_("%s: short block %d byte\n"),
1573 _("%s: short block %d bytes\n"), bytes_read),
1574 get_pname(), (int)bytes_read);
1575 print_header(stdout, file);
1576 error(_("Can't read file header"));
1580 /* bytes_read == buflen */
1581 parse_file_header(buffer, file, (size_t)bytes_read);
1594 extract_files_child(
1596 EXTRACT_LIST * elist)
1601 GPtrArray *argv_ptr = g_ptr_array_new();
1603 EXTRACT_LIST_ITEM *fn;
1604 enum dumptypes dumptype = IS_UNKNOWN;
1605 char buffer[DISK_BLOCK_BYTES];
1609 guint passwd_field = 999999999;
1611 char *domain = NULL, *smbpass = NULL;
1614 /* code executed by child to do extraction */
1617 /* make in_fd be our stdin */
1618 if (dup2(in_fd, STDIN_FILENO) == -1)
1620 error(_("dup2 failed in extract_files_child: %s"), strerror(errno));
1624 /* read the file header */
1626 read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO);
1628 if(file.type != F_DUMPFILE) {
1629 print_header(stdout, &file);
1630 error(_("bad header"));
1634 if (file.program != NULL) {
1636 if (strcmp(file.program, GNUTAR) == 0)
1637 dumptype = IS_GNUTAR;
1640 if (dumptype == IS_UNKNOWN) {
1641 len_program = strlen(file.program);
1642 if(len_program >= 3 &&
1643 strcmp(&file.program[len_program-3],"tar") == 0)
1648 if (dumptype == IS_UNKNOWN && strcmp(file.program, SAMBA_CLIENT) ==0) {
1649 if (samba_extract_method == SAMBA_TAR)
1650 dumptype = IS_SAMBA_TAR;
1652 dumptype = IS_SAMBA;
1657 /* form the arguments to restore */
1658 files_off_tape = length_of_tape_list(elist);
1662 g_ptr_array_add(argv_ptr, stralloc("smbclient"));
1663 smbpass = findpass(file.disk, &domain);
1665 g_ptr_array_add(argv_ptr, stralloc(file.disk));
1666 g_ptr_array_add(argv_ptr, stralloc("-U"));
1667 passwd_field = argv_ptr->len;
1668 g_ptr_array_add(argv_ptr, stralloc(smbpass));
1670 g_ptr_array_add(argv_ptr, stralloc("-W"));
1671 g_ptr_array_add(argv_ptr, stralloc(domain));
1674 g_ptr_array_add(argv_ptr, stralloc("-d0"));
1675 g_ptr_array_add(argv_ptr, stralloc("-Tx"));
1676 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1681 g_ptr_array_add(argv_ptr, stralloc("tar"));
1682 g_ptr_array_add(argv_ptr, stralloc("--numeric-owner"));
1683 g_ptr_array_add(argv_ptr, stralloc("-xpGvf"));
1684 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1687 g_ptr_array_add(argv_ptr, stralloc("tar"));
1688 g_ptr_array_add(argv_ptr, stralloc("-xpvf"));
1689 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1693 g_ptr_array_add(argv_ptr, stralloc("restore"));
1695 g_ptr_array_add(argv_ptr, stralloc("-xB"));
1697 #if defined(XFSDUMP)
1698 if (strcmp(file.program, XFSDUMP) == 0) {
1699 g_ptr_array_add(argv_ptr, stralloc("-v"));
1700 g_ptr_array_add(argv_ptr, stralloc("silent"));
1704 if (strcmp(file.program, VDUMP) == 0) {
1705 g_ptr_array_add(argv_ptr, stralloc("xf"));
1706 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1710 g_ptr_array_add(argv_ptr, stralloc("xbf"));
1711 g_ptr_array_add(argv_ptr, stralloc("2")); /* read in units of 1K */
1712 g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */
1717 for (i = 0, fn = elist->files; i < files_off_tape; i++, fn = fn->next)
1724 if (strcmp(fn->path, "/") == 0)
1725 g_ptr_array_add(argv_ptr, stralloc("."));
1727 g_ptr_array_add(argv_ptr, stralloc2(".", fn->path));
1731 #if defined(XFSDUMP)
1732 if (strcmp(file.program, XFSDUMP) == 0) {
1734 * xfsrestore needs a -s option before each file to be
1735 * restored, and also wants them to be relative paths.
1737 g_ptr_array_add(argv_ptr, stralloc("-s"));
1738 g_ptr_array_add(argv_ptr, stralloc(fn->path + 1));
1742 g_ptr_array_add(argv_ptr, stralloc(fn->path));
1746 #if defined(XFSDUMP)
1747 if (strcmp(file.program, XFSDUMP) == 0) {
1748 g_ptr_array_add(argv_ptr, stralloc("-"));
1749 g_ptr_array_add(argv_ptr, stralloc("."));
1752 g_ptr_array_add(argv_ptr, NULL);
1757 cmd = stralloc(SAMBA_CLIENT);
1760 /* fall through to ... */
1766 g_fprintf(stderr, _("warning: GNUTAR program not available.\n"));
1767 cmd = stralloc("tar");
1769 cmd = stralloc(GNUTAR);
1776 if (strcmp(file.program, DUMP) == 0) {
1777 cmd = stralloc(RESTORE);
1781 if (strcmp(file.program, VDUMP) == 0) {
1782 cmd = stralloc(VRESTORE);
1786 if (strcmp(file.program, VXDUMP) == 0) {
1787 cmd = stralloc(VXRESTORE);
1790 #if defined(XFSDUMP)
1791 if (strcmp(file.program, XFSDUMP) == 0) {
1792 cmd = stralloc(XFSRESTORE);
1796 g_fprintf(stderr, _("warning: restore program for %s not available.\n"),
1798 cmd = stralloc("restore");
1802 dbprintf(_("Exec'ing %s with arguments:\n"), cmd);
1803 for (j = 0; j < argv_ptr->len - 1; j++) {
1804 if( j == passwd_field)
1805 dbprintf("\tXXXXX\n");
1807 dbprintf("\t%s\n", (char *)g_ptr_array_index(argv_ptr, j));
1810 (void)execv(cmd, (char **)argv_ptr->pdata);
1811 /* only get here if exec failed */
1813 g_ptr_array_free_full(argv_ptr);
1815 perror(_("amrecover couldn't exec"));
1816 g_fprintf(stderr, _(" problem executing %s\n"), cmd);
1824 * Interpose something between the process writing out the dump (writing it to
1825 * some extraction program, really) and the socket from which we're reading, so
1826 * that we can do things like prompt for human interaction for multiple tapes.
1829 writer_intermediary(
1832 EXTRACT_LIST * elist)
1836 char buffer[DISK_BLOCK_BYTES];
1838 amwait_t extractor_status;
1840 SELECT_ARG_TYPE readset, selectset;
1841 struct timeval timeout;
1844 * If there's no distinct data channel (such as if we're talking to an
1845 * older server), don't bother doing anything complicated. Just run the
1849 extract_files_child(ctl_fd, elist);
1853 if(pipe(child_pipe) == -1) {
1854 error(_("extract_list - error setting up pipe to extractor: %s\n"),
1859 /* okay, ready to extract. fork a child to do the actual work */
1860 if ((pid = fork()) == 0) {
1861 /* this is the child process */
1862 /* never gets out of this clause */
1863 aclose(child_pipe[1]);
1864 extract_files_child(child_pipe[0], elist);
1868 /* This is the parent */
1870 error(_("writer_intermediary - error forking child"));
1874 aclose(child_pipe[0]);
1876 if(data_fd > ctl_fd) max_fd = data_fd+1;
1877 else max_fd = ctl_fd+1;
1879 FD_SET(data_fd, &readset);
1880 FD_SET(ctl_fd, &readset);
1883 timeout.tv_sec = READ_TIMEOUT;
1884 timeout.tv_usec = 0;
1885 FD_COPY(&readset, &selectset);
1887 nfound = select(max_fd, &selectset, NULL, NULL,
1890 g_fprintf(stderr,_("select error: %s\n"), strerror(errno));
1894 if (nfound == 0) { /* timeout */
1895 g_fprintf(stderr, _("timeout waiting %d seconds for restore\n"),
1897 g_fprintf(stderr, _("increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n"));
1901 if(FD_ISSET(ctl_fd, &selectset)) {
1902 bytes_read = read(ctl_fd, buffer, sizeof(buffer)-1);
1903 switch(bytes_read) {
1905 if ((errno != EINTR) && (errno != EAGAIN)) {
1906 if (errno != EPIPE) {
1907 g_fprintf(stderr,_("writer ctl fd read error: %s"),
1910 FD_CLR(ctl_fd, &readset);
1915 FD_CLR(ctl_fd, &readset);
1919 char desired_tape[MAX_TAPE_LABEL_BUF];
1921 buffer[bytes_read] = '\0';
1922 /* if prompted for a tape, relay said prompt to the user */
1923 if(sscanf(buffer, "FEEDME %132s\n", desired_tape) == 1) {
1927 g_printf(_("Please insert tape %s. Continue? [Y|n]: "),
1931 input = agets(stdin); /* strips \n */
1932 if (strcasecmp("", input) == 0||
1933 strcasecmp("y", input) == 0||
1934 strcasecmp("yes", input) == 0) {
1935 send_to_tape_server(tape_control_sock, "OK");
1937 } else if (strcasecmp("n", input) == 0||
1938 strcasecmp("no", input) == 0) {
1939 send_to_tape_server(tape_control_sock, "ERROR");
1941 We are the middle process, so just die. */
1947 g_fprintf(stderr, _("Strange message from tape server: %s"), buffer);
1954 /* now read some dump data */
1955 if(FD_ISSET(data_fd, &selectset)) {
1956 bytes_read = read(data_fd, buffer, sizeof(buffer)-1);
1957 switch(bytes_read) {
1959 if ((errno != EINTR) && (errno != EAGAIN)) {
1960 if (errno != EPIPE) {
1961 g_fprintf(stderr,_("writer data fd read error: %s"),
1964 FD_CLR(data_fd, &readset);
1969 FD_CLR(data_fd, &readset);
1974 * spit what we got from the server to the child
1975 * process handling actual dumpfile extraction
1977 if(full_write(child_pipe[1], buffer, bytes_read) < bytes_read) {
1978 if(errno == EPIPE) {
1979 error(_("pipe data reader has quit: %s\n"),
1983 error(_("Write error to extract child: %s\n"),
1990 } while(FD_ISSET(ctl_fd, &readset) || FD_ISSET(data_fd, &readset));
1992 aclose(child_pipe[1]);
1994 waitpid(pid, &extractor_status, 0);
1995 if(WEXITSTATUS(extractor_status) != 0){
1996 int ret = WEXITSTATUS(extractor_status);
1997 if(ret == 255) ret = -1;
1998 error(_("Extractor child exited with status %d\n"), ret);
2005 /* exec restore to do the actual restoration */
2007 /* does the actual extraction of files */
2009 * The original design had the dump image being returned exactly as it
2010 * appears on the tape, and this routine getting from the index server
2011 * whether or not it is compressed, on the assumption that the tape
2012 * server may not know how to uncompress it. But
2013 * - Amrestore can't do that. It returns either compressed or uncompressed
2014 * (always). Amrestore assumes it can uncompress files. It is thus a good
2015 * idea to run the tape server on a machine with gzip.
2016 * - The information about compression in the disklist is really only
2017 * for future dumps. It is possible to change compression on a drive
2018 * so the information in the disklist may not necessarily relate to
2019 * the dump image on the tape.
2020 * Consequently the design was changed to assuming that amrestore can
2021 * uncompress any dump image and have it return an uncompressed file
2027 EXTRACT_LIST *elist;
2029 amwait_t child_stat;
2034 tapelist_t *tlist = NULL;
2036 if (!is_extract_list_nonempty())
2038 g_printf(_("Extract list empty - No files to extract!\n"));
2042 clean_extract_list();
2044 /* get tape device name from index server if none specified */
2045 if (tape_server_name == NULL) {
2046 tape_server_name = newstralloc(tape_server_name, server_name);
2048 if (tape_device_name == NULL) {
2049 if (send_command("TAPE") == -1)
2051 if (get_reply_line() == -1)
2054 if (!server_happy())
2056 g_printf("%s\n", l);
2059 /* skip reply number */
2060 tape_device_name = newstralloc(tape_device_name, l+4);
2063 if (strcmp(tape_device_name, "/dev/null") == 0)
2065 g_printf(_("amrecover: warning: using %s as the tape device will not work\n"),
2070 for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist))
2071 if(elist->tape[0]!='/') {
2073 g_printf(_("\nExtracting files using tape drive %s on host %s.\n"),
2074 tape_device_name, tape_server_name);
2075 g_printf(_("The following tapes are needed:"));
2080 tlist = unmarshal_tapelist_str(elist->tape);
2081 for( ; tlist != NULL; tlist = tlist->next)
2082 g_printf(" %s", tlist->label);
2087 for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist))
2089 if(elist->tape[0]=='/') {
2091 g_printf(_("\nExtracting files from holding disk on host %s.\n"),
2093 g_printf(_("The following files are needed:"));
2098 tlist = unmarshal_tapelist_str(elist->tape);
2099 for( ; tlist != NULL; tlist = tlist->next)
2100 g_printf(" %s", tlist->label);
2107 if (getcwd(buf, sizeof(buf)) == NULL) {
2108 perror(_("extract_list: Cannot determine current working directory"));
2112 g_printf(_("Restoring files into directory %s\n"), buf);
2114 if (samba_extract_method == SAMBA_SMBCLIENT)
2115 g_printf(_("(unless it is a Samba backup, that will go through to the SMB server)\n"));
2117 if (!okay_to_continue(0,0,0))
2121 while ((elist = first_tape_list()) != NULL)
2123 if(elist->tape[0]=='/') {
2124 dump_device_name = newstralloc(dump_device_name, elist->tape);
2125 g_printf(_("Extracting from file "));
2126 tlist = unmarshal_tapelist_str(dump_device_name);
2127 for( ; tlist != NULL; tlist = tlist->next)
2128 g_printf(" %s", tlist->label);
2133 g_printf(_("Extracting files using tape drive %s on host %s.\n"),
2134 tape_device_name, tape_server_name);
2135 tlist = unmarshal_tapelist_str(elist->tape);
2136 g_printf(_("Load tape %s now\n"), tlist->label);
2138 otc = okay_to_continue(1,1,0);
2141 else if (otc == SKIP_TAPE) {
2142 delete_tape_list(elist); /* skip this tape */
2145 dump_device_name = newstralloc(dump_device_name, tape_device_name);
2147 dump_datestamp = newstralloc(dump_datestamp, elist->date);
2149 /* connect to the tape handler daemon on the tape drive server */
2150 if ((tape_control_sock = extract_files_setup(elist->tape, elist->fileno)) == -1)
2152 g_fprintf(stderr, _("amrecover - can't talk to tape server\n"));
2156 /* okay, ready to extract. fork a child to do the actual work */
2157 if ((pid = fork()) == 0)
2159 /* this is the child process */
2160 /* never gets out of this clause */
2161 writer_intermediary(tape_control_sock, tape_data_sock, elist);
2164 /* this is the parent */
2167 perror(_("extract_list - error forking child"));
2168 aclose(tape_control_sock);
2172 /* store the child pid globally so that it can be killed on intr */
2173 extract_restore_child_pid = pid;
2175 /* wait for the child process to finish */
2176 if ((pid = waitpid(-1, &child_stat, 0)) == (pid_t)-1)
2178 perror(_("extract_list - error waiting for child"));
2182 if(tape_data_sock != -1) {
2183 aclose(tape_data_sock);
2186 if (pid == extract_restore_child_pid)
2188 extract_restore_child_pid = -1;
2192 g_fprintf(stderr, _("extract list - unknown child terminated?\n"));
2195 if ((WIFEXITED(child_stat) != 0) && (WEXITSTATUS(child_stat) != 0))
2198 _("extract_list - child returned non-zero status: %d\n"),
2199 WEXITSTATUS(child_stat));
2200 otc = okay_to_continue(0,0,1);
2205 delete_tape_list(elist); /* tape failed so delete from list */
2209 delete_tape_list(elist); /* tape done so delete from list */