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.6 2006/08/24 01:57:15 paddy_s Exp $
29 * implements the "extract" command in amrecover
34 #include "amrecover.h"
35 #include "fileheader.h"
44 typedef struct EXTRACT_LIST_ITEM {
47 struct EXTRACT_LIST_ITEM *next;
51 typedef struct EXTRACT_LIST {
52 char *date; /* date tape created */
53 int level; /* level of dump */
54 char *tape; /* tape label */
55 off_t fileno; /* fileno on tape */
56 EXTRACT_LIST_ITEM *files; /* files to get off tape */
58 struct EXTRACT_LIST *next;
65 char *dump_device_name = NULL;
67 extern char *localhost;
69 /* global pid storage for interrupt handler */
70 pid_t extract_restore_child_pid = -1;
72 static EXTRACT_LIST *extract_list = NULL;
73 static int tape_control_sock = -1;
74 static int tape_data_sock = -1;
77 unsigned short samba_extract_method = SAMBA_TAR;
78 #endif /* SAMBA_CLIENT */
80 #define READ_TIMEOUT 240*60
82 EXTRACT_LIST *first_tape_list(void);
83 EXTRACT_LIST *next_tape_list(EXTRACT_LIST *list);
84 int is_extract_list_nonempty(void);
85 int length_of_tape_list(EXTRACT_LIST *tape_list);
86 void add_file(char *path, char *regex);
87 void add_glob(char *glob);
88 void add_regex(char *regex);
89 void clear_extract_list(void);
90 void clean_tape_list(EXTRACT_LIST *tape_list);
91 void clean_extract_list(void);
92 void delete_file(char *path, char *regex);
93 void delete_glob(char *glob);
94 void delete_regex(char *regex);
95 void delete_tape_list(EXTRACT_LIST *tape_list);
96 void display_extract_list(char *file);
97 void extract_files(void);
98 void read_file_header(char *buffer,
102 void writer_intermediary(int ctl_fd, int data_fd, EXTRACT_LIST *elist);
103 void writer_intermediary(int ctl_fd, int data_fd, EXTRACT_LIST *elist);
105 static int add_extract_item(DIR_ITEM *ditem);
106 static int delete_extract_item(DIR_ITEM *ditem);
107 static int extract_files_setup(char *label, off_t fsf);
108 static int okay_to_continue(int allow_tape,
111 static int okay_to_continue(int, int, int);
112 static ssize_t read_buffer(int datafd,
116 static void clear_tape_list(EXTRACT_LIST *tape_list);
117 static void extract_files_child(int in_fd, EXTRACT_LIST *elist);
118 static void send_to_tape_server(int tss, char *cmd);
122 * Function: ssize_t read_buffer(datafd, buffer, buflen, timeout_s)
125 * read data from input file desciptor waiting up to timeout_s
126 * seconds before returning data.
129 * datafd - File descriptor to read from.
130 * buffer - Buffer to read into.
131 * buflen - Maximum number of bytes to read into buffer.
132 * timeout_s - Seconds to wait before returning what was already read.
135 * >0 - Number of data bytes in buffer.
137 * -1 - errno == ETIMEDOUT if no data available in specified time.
138 * errno == ENFILE if datafd is invalid.
139 * otherwise errno is set by select or read..
151 struct timeval timeout;
156 if(datafd < 0 || datafd >= (int)FD_SETSIZE) {
157 errno = EMFILE; /* out of range */
162 spaceleft = (ssize_t)buflen;
166 FD_SET(datafd, &readset);
167 timeout.tv_sec = timeout_s;
169 nfound = select(datafd+1, &readset, NULL, NULL, &timeout);
171 /* Select returned an error. */
172 fprintf(stderr,"select error: %s\n", strerror(errno));
178 /* Select timed out. */
179 if (timeout_s != 0) {
180 /* Not polling: a real read timeout */
181 fprintf(stderr,"timeout waiting for restore\n");
182 fprintf(stderr,"increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n");
189 if(!FD_ISSET(datafd, &readset))
192 /* Select says data is available, so read it. */
193 size = read(datafd, dataptr, (size_t)spaceleft);
195 if ((errno == EINTR) || (errno == EAGAIN)) {
198 if (errno != EPIPE) {
199 fprintf(stderr, "read_buffer: read error - %s",
207 } while ((size > 0) && (spaceleft > 0));
209 return ((((ssize_t)buflen-spaceleft) > 0) ? ((ssize_t)buflen-spaceleft) : size);
214 first_tape_list(void)
221 /*@keep@*/ EXTRACT_LIST *list)
230 EXTRACT_LIST * tape_list)
232 EXTRACT_LIST_ITEM *this, *next;
235 this = tape_list->files;
243 tape_list->files = NULL;
247 /* remove a tape list from the extract list, clearing the tape list
248 beforehand if necessary */
251 EXTRACT_LIST * tape_list)
253 EXTRACT_LIST *this, *prev;
255 if (tape_list == NULL)
258 /* is it first on the list? */
259 if (tape_list == extract_list)
261 extract_list = tape_list->next;
262 clear_tape_list(tape_list);
263 amfree(tape_list->date);
264 amfree(tape_list->tape);
269 /* so not first on list - find it and delete */
271 this = extract_list->next;
274 if (this == tape_list)
276 prev->next = tape_list->next;
277 clear_tape_list(tape_list);
278 amfree(tape_list->date);
279 amfree(tape_list->tape);
290 /* return the number of files on a tape's list */
293 EXTRACT_LIST * tape_list)
295 EXTRACT_LIST_ITEM *fn;
299 for (fn = tape_list->files; fn != NULL; fn = fn->next)
307 clear_extract_list(void)
309 while (extract_list != NULL)
310 delete_tape_list(extract_list);
316 EXTRACT_LIST *tape_list)
318 EXTRACT_LIST_ITEM *fn1, *pfn1, *ofn1;
319 EXTRACT_LIST_ITEM *fn2, *pfn2, *ofn2;
324 fn1 = tape_list->files;
325 while (fn1 != NULL) {
330 while (fn2 != NULL && remove_fn1 == 0) {
332 if(strcmp(fn1->path, fn2->path) == 0) {
334 } else if (strncmp(fn1->path, fn2->path, strlen(fn1->path)) == 0 &&
335 ((strlen(fn2->path) > strlen(fn1->path) &&
336 fn2->path[strlen(fn1->path)] == '/') ||
337 (fn1->path[strlen(fn1->path)-1] == '/'))) {
339 } else if (strncmp(fn2->path, fn1->path, strlen(fn2->path)) == 0 &&
340 ((strlen(fn1->path) > strlen(fn2->path) &&
341 fn1->path[strlen(fn2->path)] == '/') ||
342 (fn2->path[strlen(fn2->path)-1] == '/'))) {
348 dbprintf(("removing path %s, it is included in %s\n",
349 fn2->path, fn1->path));
355 } else if (remove_fn1 == 0) {
361 if(remove_fn1 != 0) {
362 /* fn2->path is always valid */
363 /*@i@*/ dbprintf(("removing path %s, it is included in %s\n",
364 /*@i@*/ fn1->path, fn2->path));
369 amfree(tape_list->files);
370 tape_list->files = fn1;
384 clean_extract_list(void)
388 for (this = extract_list; this != NULL; this = this->next)
389 clean_tape_list(this);
393 /* returns -1 if error */
394 /* returns 0 on succes */
395 /* returns 1 if already added */
400 EXTRACT_LIST *this, *this1;
401 EXTRACT_LIST_ITEM *that, *curr;
402 char *ditem_path = NULL;
404 ditem_path = stralloc(ditem->path);
405 clean_pathname(ditem_path);
407 for (this = extract_list; this != NULL; this = this->next)
409 /* see if this is the list for the tape */
410 if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
412 /* yes, so add to list */
416 if (strcmp(curr->path, ditem_path) == 0) {
422 that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
423 that->path = stralloc(ditem_path);
424 that->next = this->files;
425 this->files = that; /* add at front since easiest */
431 /* so this is the first time we have seen this tape */
432 this = (EXTRACT_LIST *)alloc(sizeof(EXTRACT_LIST));
433 this->tape = stralloc(ditem->tape);
434 this->level = ditem->level;
435 this->fileno = ditem->fileno;
436 this->date = stralloc(ditem->date);
437 that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
438 that->path = stralloc(ditem_path);
442 /* add this in date increasing order */
443 /* because restore must be done in this order */
444 /* add at begining */
445 if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0)
447 this->next = extract_list;
452 for (this1 = extract_list; this1->next != NULL; this1 = this1->next)
454 /* add in the middle */
455 if(strcmp(this->date,this1->next->date) < 0)
457 this->next = this1->next;
471 /* returns -1 if error */
472 /* returns 0 on deletion */
473 /* returns 1 if not there */
479 EXTRACT_LIST_ITEM *that, *prev;
480 char *ditem_path = NULL;
482 ditem_path = stralloc(ditem->path);
483 clean_pathname(ditem_path);
485 for (this = extract_list; this != NULL; this = this->next)
487 /* see if this is the list for the tape */
488 if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
490 /* yes, so find file on list */
492 if (strcmp(that->path, ditem_path) == 0)
495 this->files = that->next;
498 /* if list empty delete it */
499 if (this->files == NULL)
500 delete_tape_list(this);
508 if (strcmp(that->path, ditem_path) == 0)
510 prev->next = that->next;
535 char *uqglob = unquote_string(glob);
537 regex = glob_to_regex(uqglob);
538 dbprintf(("add_glob (%s) -> %s\n", uqglob, regex));
539 if ((s = validate_regexp(regex)) != NULL) {
540 printf("%s is not a valid shell wildcard pattern: ", glob);
544 * glob_to_regex() anchors the beginning of the pattern with ^,
545 * but we will be tacking it onto the end of the current directory
546 * in add_file, so strip that off. Also, it anchors the end with
547 * $, but we need to match an optional trailing /, so tack that on
550 regex_path = stralloc(regex + 1);
551 regex_path[strlen(regex_path) - 1] = '\0';
552 strappend(regex_path, "[/]*$");
553 add_file(uqglob, regex_path);
565 char *uqregex = unquote_string(regex);
567 if ((s = validate_regexp(uqregex)) != NULL) {
568 printf("%s is not a valid regular expression: ", regex);
571 add_file(uqregex, regex);
580 DIR_ITEM *ditem, lditem;
581 char *path_on_disk = NULL;
582 char *path_on_disk_slash = NULL;
587 char *dir, *dir_undo, dir_undo_ch = '\0';
588 char *ditem_path = NULL;
591 char *s, *fp, *quoted;
596 if (disk_path == NULL) {
597 printf("Must select directory before adding files\n");
600 memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
602 dbprintf(("add_file: Looking for \"%s\"\n", regex));
604 if(strcmp(regex, "/[/]*$") == 0) { /* "/" behave like "." */
607 else if(strcmp(regex, "[^/]*[/]*$") == 0) { /* "*" */
608 regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
610 /* remove "/" at end of path */
611 j = (ssize_t)(strlen(regex) - 1);
612 while(j >= 0 && regex[j] == '/')
616 /* convert path (assumed in cwd) to one on disk */
617 if (strcmp(disk_path, "/") == 0) {
619 /* No mods needed if already starts with '/' */
620 path_on_disk = stralloc(regex);
623 path_on_disk = stralloc2("/", regex);
626 char *clean_disk_path = clean_regex(disk_path);
627 path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
628 amfree(clean_disk_path);
631 path_on_disk_slash = stralloc2(path_on_disk, "/");
633 dbprintf(("add_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n",
634 regex, path_on_disk));
638 for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
641 quoted = quote_string(ditem->path);
642 dbprintf(("add_file: Pondering ditem->path=%s\n", quoted));
644 if (match(path_on_disk, ditem->path)
645 || match(path_on_disk_slash, ditem->path))
648 j = (ssize_t)strlen(ditem->path);
649 if((j > 0 && ditem->path[j-1] == '/')
650 || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
651 { /* It is a directory */
652 ditem_path = newstralloc(ditem_path, ditem->path);
653 clean_pathname(ditem_path);
655 cmd = stralloc2("ORLD ", ditem_path);
656 if(send_command(cmd) == -1) {
659 amfree(path_on_disk);
660 amfree(path_on_disk_slash);
666 if ((i = get_reply_line()) == -1) {
668 amfree(path_on_disk);
669 amfree(path_on_disk_slash);
672 if(i==0) { /* assume something wrong */
674 amfree(path_on_disk);
675 amfree(path_on_disk_slash);
682 lditem.path = newstralloc(lditem.path, ditem->path);
683 /* skip the last line -- duplicate of the preamble */
685 while ((i = get_reply_line()) != 0) {
688 amfree(path_on_disk);
689 amfree(path_on_disk_slash);
694 if(dir_undo) *dir_undo = dir_undo_ch;
696 cmd = stralloc(l); /* save for error report */
698 continue; /* throw the rest of the lines away */
701 if (!server_happy()) {
707 if(strncmp_const_skip(l, "201-", s, ch) != 0) {
708 err = "bad reply: not 201-";
713 skip_whitespace(s, ch);
715 err = "bad reply: missing date field";
719 skip_non_whitespace(s, ch);
721 lditem.date = newstralloc(lditem.date, fp);
724 skip_whitespace(s, ch);
725 if(ch == '\0' || sscanf(s - 1, "%d", &lditem.level) != 1) {
726 err = "bad reply: cannot parse level field";
731 skip_whitespace(s, ch);
733 err = "bad reply: missing tape field";
737 skip_non_whitespace(s, ch);
739 lditem.tape = newstralloc(lditem.tape, fp);
742 if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
743 OFF_T_FMT_TYPE fileno_ = (OFF_T_FMT_TYPE)0;
744 skip_whitespace(s, ch);
746 sscanf(s - 1, OFF_T_FMT, &fileno_) != 1) {
747 err = "bad reply: cannot parse fileno field";
750 lditem.fileno = (off_t)fileno_;
754 skip_whitespace(s, ch);
756 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 printf("System error\n");
768 dbprintf(("add_file: (Failed) System error\n"));
772 quoted = quote_string(lditem.path);
773 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 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 printf("System error\n");
804 dbprintf(("add_file: (Failed) System error\n"));
808 quoted = quote_string(ditem->path);
809 printf("Added file %s\n", quoted);
810 dbprintf(("add_file: (Successful) Added %s\n", quoted));
815 quoted = quote_string(ditem->path);
816 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 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 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 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';
905 char *ditem_path = NULL;
913 if (disk_path == NULL) {
914 printf("Must select directory before deleting files\n");
917 memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
919 dbprintf(("delete_file: Looking for \"%s\"\n", path));
921 if (strcmp(regex, "[^/]*[/]*$") == 0) {
922 /* Looking for * find everything but single . */
923 regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
925 /* remove "/" at end of path */
926 j = (ssize_t)(strlen(regex) - 1);
927 while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
930 /* convert path (assumed in cwd) to one on disk */
931 if (strcmp(disk_path, "/") == 0) {
933 if (strcmp(regex, "/[/]*$") == 0) {
934 /* We want "/" to match the directory itself: "/." */
935 path_on_disk = stralloc("/\\.[/]*$");
937 /* No mods needed if already starts with '/' */
938 path_on_disk = stralloc(regex);
942 path_on_disk = stralloc2("/", regex);
945 char *clean_disk_path = clean_regex(disk_path);
946 path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
947 amfree(clean_disk_path);
950 path_on_disk_slash = stralloc2(path_on_disk, "/");
952 dbprintf(("delete_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n",
953 regex, path_on_disk));
955 for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
957 quoted = quote_string(ditem->path);
958 dbprintf(("delete_file: Pondering ditem->path=%s\n", quoted));
960 if (match(path_on_disk, ditem->path)
961 || match(path_on_disk_slash, ditem->path))
964 j = (ssize_t)strlen(ditem->path);
965 if((j > 0 && ditem->path[j-1] == '/')
966 || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
967 { /* It is a directory */
968 ditem_path = newstralloc(ditem_path, ditem->path);
969 clean_pathname(ditem_path);
971 cmd = stralloc2("ORLD ", ditem_path);
972 if(send_command(cmd) == -1) {
975 amfree(path_on_disk);
976 amfree(path_on_disk_slash);
981 if ((i = get_reply_line()) == -1) {
983 amfree(path_on_disk);
984 amfree(path_on_disk_slash);
987 if(i==0) /* assume something wrong */
990 amfree(path_on_disk);
991 amfree(path_on_disk_slash);
997 lditem.path = newstralloc(lditem.path, ditem->path);
999 tape_undo = dir_undo = NULL;
1000 /* skip the last line -- duplicate of the preamble */
1001 while ((i = get_reply_line()) != 0)
1005 amfree(path_on_disk);
1006 amfree(path_on_disk_slash);
1011 if(tape_undo) *tape_undo = tape_undo_ch;
1012 if(dir_undo) *dir_undo = dir_undo_ch;
1013 tape_undo = dir_undo = NULL;
1014 cmd = stralloc(l); /* save for the error report */
1016 continue; /* throw the rest of the lines away */
1019 if (!server_happy()) {
1025 if(strncmp_const_skip(l, "201-", s, ch) != 0) {
1026 err = "bad reply: not 201-";
1031 skip_whitespace(s, ch);
1033 err = "bad reply: missing date field";
1037 skip_non_whitespace(s, ch);
1040 skip_whitespace(s, ch);
1041 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1042 err = "bad reply: cannot parse level field";
1045 skip_integer(s, ch);
1047 skip_whitespace(s, ch);
1049 err = "bad reply: missing tape field";
1053 skip_non_whitespace(s, ch);
1055 tape_undo_ch = *tape_undo;
1058 if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
1059 OFF_T_FMT_TYPE fileno_ = (OFF_T_FMT_TYPE)0;
1060 skip_whitespace(s, ch);
1062 sscanf(s - 1, OFF_T_FMT, &fileno_) != 1) {
1063 err = "bad reply: cannot parse fileno field";
1066 fileno = (off_t)fileno_;
1067 skip_integer(s, ch);
1070 skip_whitespace(s, ch);
1072 err = "bad reply: missing directory field";
1075 skip_non_whitespace(s, ch);
1077 dir_undo_ch = *dir_undo;
1080 lditem.date = newstralloc(lditem.date, date);
1082 lditem.tape = newstralloc(lditem.tape, tape);
1083 switch(delete_extract_item(&lditem)) {
1085 printf("System error\n");
1086 dbprintf(("delete_file: (Failed) System error\n"));
1089 printf("Deleted dir %s at date %s\n", ditem_path, date);
1090 dbprintf(("delete_file: (Successful) Deleted dir %s at date %s\n",
1098 if(!server_happy()) {
1105 } else if(deleted == 0) {
1106 printf("Warning - dir '%s' not on tape list\n",
1108 dbprintf(("delete_file: dir '%s' not on tape list\n",
1114 switch(delete_extract_item(ditem)) {
1116 printf("System error\n");
1117 dbprintf(("delete_file: (Failed) System error\n"));
1120 printf("Deleted %s\n", ditem->path);
1121 dbprintf(("delete_file: (Successful) Deleted %s\n",
1125 printf("Warning - file '%s' not on tape list\n",
1127 dbprintf(("delete_file: file '%s' not on tape list\n",
1136 amfree(path_on_disk);
1137 amfree(path_on_disk_slash);
1140 printf("File %s doesn't exist in directory\n", path);
1141 dbprintf(("delete_file: (Failed) File %s doesn't exist in directory\n",
1147 /* print extract list into file. If NULL ptr passed print to screen */
1149 display_extract_list(
1153 EXTRACT_LIST_ITEM *that;
1156 char *pager_command;
1161 if ((pager = getenv("PAGER")) == NULL)
1166 * Set up the pager command so if the pager is terminated, we do
1167 * not get a SIGPIPE back.
1169 pager_command = stralloc2(pager, " ; /bin/cat > /dev/null");
1170 if ((fp = popen(pager_command, "w")) == NULL)
1172 printf("Warning - can't pipe through %s\n", pager);
1175 amfree(pager_command);
1179 uqfile = unquote_string(file);
1180 if ((fp = fopen(uqfile, "w")) == NULL)
1182 printf("Can't open file %s to print extract list into\n", file);
1189 for (this = extract_list; this != NULL; this = this->next)
1191 fprintf(fp, "TAPE %s LEVEL %d DATE %s\n",
1192 this->tape, this->level, this->date);
1193 for (that = this->files; that != NULL; that = that->next)
1194 fprintf(fp, "\t%s\n", that->path);
1200 printf("Extract list written to file %s\n", file);
1206 /* returns 0 if extract list empty and 1 if it isn't */
1208 is_extract_list_nonempty(void)
1210 return (extract_list != NULL);
1214 /* prints continue prompt and waits for response,
1215 returns 0 if don't, non-0 if do */
1232 prompt = "New tape device [?]: ";
1233 } else if (allow_tape && allow_skip) {
1234 prompt = "Continue [?/Y/n/s/t]? ";
1235 } else if (allow_tape && !allow_skip) {
1236 prompt = "Continue [?/Y/n/t]? ";
1237 } else if (allow_retry) {
1238 prompt = "Continue [?/Y/n/r]? ";
1240 prompt = "Continue [?/Y/n]? ";
1242 fputs(prompt, stdout);
1243 fflush(stdout); fflush(stderr);
1245 if ((line = agets(stdin)) == NULL) {
1256 while ((ch = *s++) != '\0' && isspace(ch)) {
1257 (void)ch; /* Quiet empty loop body warning */
1261 printf("Enter a new device ([host:]device) or \"default\"\n");
1263 printf("Enter \"y\"es to continue, \"n\"o to stop");
1265 printf(", \"s\"kip this tape");
1268 printf(" or \"r\"etry this tape");
1271 printf(" or \"t\"ape to change tape drives");
1275 } else if (get_tape) {
1278 } else if (ch == '\0' || ch == 'Y' || ch == 'y') {
1280 } else if (allow_tape && (ch == 'T' || ch == 't')) {
1282 } else if (ch == 'N' || ch == 'n') {
1284 } else if (allow_retry && (ch == 'R' || ch == 'r')) {
1286 } else if (allow_skip && (ch == 'S' || ch == 's')) {
1297 send_to_tape_server(
1301 char *msg = stralloc2(cmd, "\r\n");
1303 if (fullwrite(tss, msg, strlen(msg)) < 0)
1305 error("Error writing to tape server");
1312 /* start up connection to tape server and set commands to initiate
1313 transfer of dump image.
1314 Return tape server socket on success, -1 on error. */
1316 extract_files_setup(
1321 in_port_t my_port, my_data_port;
1322 char *disk_regex = NULL;
1323 char *host_regex = NULL;
1324 char *service_name = NULL;
1326 char *clean_datestamp, *ch, *ch1;
1327 char *our_feature_string = NULL;
1330 service_name = stralloc2("amidxtape", SERVICE_SUFFIX);
1332 /* get tape server details */
1333 if ((sp = getservbyname(service_name, "tcp")) == NULL)
1335 printf("%s/tcp unknown protocol - config error?\n", service_name);
1336 amfree(service_name);
1339 amfree(service_name);
1340 seteuid(0); /* it either works ... */
1342 tape_control_sock = stream_client_privileged(tape_server_name,
1343 (in_port_t)ntohs((in_port_t)sp->s_port),
1348 if (tape_control_sock < 0)
1350 printf("cannot connect to %s: %s\n", tape_server_name, strerror(errno));
1353 if (my_port >= IPPORT_RESERVED) {
1354 aclose(tape_control_sock);
1355 printf("did not get a reserved port: %u\n", (unsigned)my_port);
1360 seteuid(getuid()); /* put it back */
1362 /* do the security thing */
1363 line = get_security();
1364 send_to_tape_server(tape_control_sock, line);
1365 memset(line, '\0', strlen(line));
1368 disk_regex = alloc(strlen(disk_name) * 2 + 3);
1373 /* we want to force amrestore to only match disk_name exactly */
1376 /* We need to escape some characters first... NT compatibilty crap */
1377 for (; *ch != 0; ch++, ch1++) {
1378 switch (*ch) { /* done this way in case there are more */
1381 /* no break; we do want to fall through... */
1387 /* we want to force amrestore to only match disk_name exactly */
1392 host_regex = alloc(strlen(dump_hostname) * 2 + 3);
1397 /* we want to force amrestore to only match dump_hostname exactly */
1400 /* We need to escape some characters first... NT compatibilty crap */
1401 for (; *ch != 0; ch++, ch1++) {
1402 switch (*ch) { /* done this way in case there are more */
1405 /* no break; we do want to fall through... */
1411 /* we want to force amrestore to only match dump_hostname exactly */
1416 clean_datestamp = stralloc(dump_datestamp);
1417 for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) {
1425 /* push our feature list off to the tape server */
1426 /* XXX assumes that index server and tape server are equivalent, ew */
1427 if(am_has_feature(indexsrv_features, fe_amidxtaped_exchange_features)){
1428 char buffer[32768] = "\0";
1430 our_feature_string = am_feature_to_string(our_features);
1431 tt = newstralloc2(tt, "FEATURES=", our_feature_string);
1432 send_to_tape_server(tape_control_sock, tt);
1433 if (read(tape_control_sock, buffer, sizeof(buffer)) <= 0) {
1434 error("Could not read features from control socket\n");
1437 tapesrv_features = am_string_to_feature(buffer);
1438 amfree(our_feature_string);
1442 if(am_has_feature(indexsrv_features, fe_amidxtaped_header) &&
1443 am_has_feature(indexsrv_features, fe_amidxtaped_device) &&
1444 am_has_feature(indexsrv_features, fe_amidxtaped_host) &&
1445 am_has_feature(indexsrv_features, fe_amidxtaped_disk) &&
1446 am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) {
1448 if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) {
1449 tt = newstralloc2(tt, "CONFIG=", config);
1450 send_to_tape_server(tape_control_sock, tt);
1452 if(am_has_feature(indexsrv_features, fe_amidxtaped_label) &&
1453 label && label[0] != '/') {
1454 tt = newstralloc2(tt,"LABEL=",label);
1455 send_to_tape_server(tape_control_sock, tt);
1457 if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) {
1459 snprintf(v_fsf, 99, OFF_T_FMT, (OFF_T_FMT_TYPE)fsf);
1460 tt = newstralloc2(tt, "FSF=",v_fsf);
1461 send_to_tape_server(tape_control_sock, tt);
1463 send_to_tape_server(tape_control_sock, "HEADER");
1464 tt = newstralloc2(tt, "DEVICE=", dump_device_name);
1465 send_to_tape_server(tape_control_sock, tt);
1466 tt = newstralloc2(tt, "HOST=", host_regex);
1467 send_to_tape_server(tape_control_sock, tt);
1468 tt = newstralloc2(tt, "DISK=", disk_regex);
1469 send_to_tape_server(tape_control_sock, tt);
1470 tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp);
1471 send_to_tape_server(tape_control_sock, tt);
1472 send_to_tape_server(tape_control_sock, "END");
1475 else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) {
1476 /* send to the tape server what tape file we want */
1485 send_to_tape_server(tape_control_sock, "6");
1486 send_to_tape_server(tape_control_sock, "-h");
1487 send_to_tape_server(tape_control_sock, "-p");
1488 send_to_tape_server(tape_control_sock, dump_device_name);
1489 send_to_tape_server(tape_control_sock, host_regex);
1490 send_to_tape_server(tape_control_sock, disk_regex);
1491 send_to_tape_server(tape_control_sock, clean_datestamp);
1493 dbprintf(("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n",
1494 dump_device_name, host_regex, disk_regex, clean_datestamp));
1498 * split-restoring amidxtaped versions will expect to set up a data
1499 * connection for dumpfile data, distinct from the socket we're already
1500 * using for control data
1503 if(am_has_feature(tapesrv_features, fe_recover_splits)){
1505 in_port_t data_port = (in_port_t)-1;
1508 nread = read(tape_control_sock, buffer, sizeof(buffer));
1511 error("Could not read from control socket: %s\n",
1516 buffer[nread] = '\0';
1517 if (sscanf(buffer, "CONNECT %hu\n",
1518 (unsigned short *)&data_port) != 1) {
1519 error("Recieved invalid port number message from control socket: %s\n",
1524 tape_data_sock = stream_client_privileged(server_name,
1530 if(tape_data_sock == -1){
1531 error("Unable to make data connection to server: %s\n",
1536 amfree(our_feature_string);
1538 line = get_security();
1540 send_to_tape_server(tape_data_sock, line);
1541 memset(line, '\0', strlen(line));
1547 amfree(clean_datestamp);
1549 return tape_control_sock;
1554 * Reads the first block of a tape file.
1566 bytes_read = read_buffer(tapedev, buffer, buflen, READ_TIMEOUT);
1567 if(bytes_read < 0) {
1568 error("error reading header (%s), check amidxtaped.*.debug on server",
1573 if((size_t)bytes_read < buflen) {
1574 fprintf(stderr, "%s: short block %d byte%s\n",
1575 get_pname(), (int)bytes_read, (bytes_read == 1) ? "" : "s");
1576 print_header(stdout, file);
1577 error("Can't read file header");
1581 /* bytes_read == buflen */
1582 parse_file_header(buffer, file, (size_t)bytes_read);
1595 extract_files_child(
1597 EXTRACT_LIST * elist)
1600 int extra_params = 0;
1602 char **restore_args = NULL;
1604 EXTRACT_LIST_ITEM *fn;
1605 enum dumptypes dumptype = IS_UNKNOWN;
1606 char buffer[DISK_BLOCK_BYTES];
1610 int passwd_field = -1;
1612 char *domain = NULL, *smbpass = NULL;
1615 /* code executed by child to do extraction */
1618 /* make in_fd be our stdin */
1619 if (dup2(in_fd, STDIN_FILENO) == -1)
1621 error("dup2 failed in extract_files_child: %s", strerror(errno));
1625 /* read the file header */
1627 read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO);
1629 if(file.type != F_DUMPFILE) {
1630 print_header(stdout, &file);
1631 error("bad header");
1635 if (file.program != NULL) {
1637 if (strcmp(file.program, GNUTAR) == 0)
1638 dumptype = IS_GNUTAR;
1641 if (dumptype == IS_UNKNOWN) {
1642 len_program = strlen(file.program);
1643 if(len_program >= 3 &&
1644 strcmp(&file.program[len_program-3],"tar") == 0)
1649 if (dumptype == IS_UNKNOWN && strcmp(file.program, SAMBA_CLIENT) ==0) {
1650 if (samba_extract_method == SAMBA_TAR)
1651 dumptype = IS_SAMBA_TAR;
1653 dumptype = IS_SAMBA;
1658 /* form the arguments to restore */
1659 files_off_tape = length_of_tape_list(elist);
1678 #if defined(XFSDUMP)
1679 if (strcmp(file.program, XFSDUMP) == 0) {
1680 extra_params = 4 + files_off_tape;
1690 restore_args = (char **)alloc((size_t)((extra_params + files_off_tape + 1)
1695 restore_args[j++] = stralloc("smbclient");
1696 smbpass = findpass(file.disk, &domain);
1698 restore_args[j++] = stralloc(file.disk);
1700 restore_args[j++] = stralloc("-U");
1701 restore_args[j++] = smbpass;
1703 restore_args[j++] = stralloc("-W");
1704 restore_args[j++] = stralloc(domain);
1709 restore_args[j++] = stralloc("-d0");
1710 restore_args[j++] = stralloc("-Tx");
1711 restore_args[j++] = stralloc("-"); /* data on stdin */
1716 restore_args[j++] = stralloc("tar");
1717 restore_args[j++] = stralloc("--numeric-owner");
1718 restore_args[j++] = stralloc("-xpGvf");
1719 restore_args[j++] = stralloc("-"); /* data on stdin */
1722 restore_args[j++] = stralloc("tar");
1723 restore_args[j++] = stralloc("-xpvf");
1724 restore_args[j++] = stralloc("-"); /* data on stdin */
1728 restore_args[j++] = stralloc("restore");
1730 restore_args[j++] = stralloc("-xB");
1732 #if defined(XFSDUMP)
1733 if (strcmp(file.program, XFSDUMP) == 0) {
1734 restore_args[j++] = stralloc("-v");
1735 restore_args[j++] = stralloc("silent");
1739 if (strcmp(file.program, VDUMP) == 0) {
1740 restore_args[j++] = stralloc("xf");
1741 restore_args[j++] = stralloc("-"); /* data on stdin */
1745 restore_args[j++] = stralloc("xbf");
1746 restore_args[j++] = stralloc("2"); /* read in units of 1K */
1747 restore_args[j++] = stralloc("-"); /* data on stdin */
1752 for (i = 0, fn = elist->files; i < files_off_tape; i++, fn = fn->next)
1759 if (strcmp(fn->path, "/") == 0)
1760 restore_args[j++] = stralloc(".");
1762 restore_args[j++] = stralloc2(".", fn->path);
1766 #if defined(XFSDUMP)
1767 if (strcmp(file.program, XFSDUMP) == 0) {
1769 * xfsrestore needs a -s option before each file to be
1770 * restored, and also wants them to be relative paths.
1772 restore_args[j++] = stralloc("-s");
1773 restore_args[j++] = stralloc(fn->path + 1);
1777 restore_args[j++] = stralloc(fn->path);
1781 #if defined(XFSDUMP)
1782 if (strcmp(file.program, XFSDUMP) == 0) {
1783 restore_args[j++] = stralloc("-");
1784 restore_args[j++] = stralloc(".");
1787 restore_args[j] = NULL;
1792 cmd = stralloc(SAMBA_CLIENT);
1795 /* fall through to ... */
1801 fprintf(stderr, "warning: GNUTAR program not available.\n");
1802 cmd = stralloc("tar");
1804 cmd = stralloc(GNUTAR);
1811 if (strcmp(file.program, DUMP) == 0) {
1812 cmd = stralloc(RESTORE);
1816 if (strcmp(file.program, VDUMP) == 0) {
1817 cmd = stralloc(VRESTORE);
1821 if (strcmp(file.program, VXDUMP) == 0) {
1822 cmd = stralloc(VXRESTORE);
1825 #if defined(XFSDUMP)
1826 if (strcmp(file.program, XFSDUMP) == 0) {
1827 cmd = stralloc(XFSRESTORE);
1831 fprintf(stderr, "warning: restore program for %s not available.\n",
1833 cmd = stralloc("restore");
1837 dbprintf(("Exec'ing %s with arguments:\n", cmd));
1838 for (i = 0; i < j; i++) {
1839 if( i == passwd_field)
1840 dbprintf(("\tXXXXX\n"));
1842 dbprintf(("\t%s\n", restore_args[i]));
1844 (void)execv(cmd, restore_args);
1845 /* only get here if exec failed */
1847 for (i = 0; i < j; i++) {
1848 amfree(restore_args[i]);
1850 amfree(restore_args);
1852 perror("amrecover couldn't exec");
1853 fprintf(stderr, " problem executing %s\n", cmd);
1861 * Interpose something between the process writing out the dump (writing it to
1862 * some extraction program, really) and the socket from which we're reading, so
1863 * that we can do things like prompt for human interaction for multiple tapes.
1866 writer_intermediary(
1869 EXTRACT_LIST * elist)
1873 char buffer[DISK_BLOCK_BYTES];
1875 amwait_t extractor_status;
1877 SELECT_ARG_TYPE readset, selectset;
1878 struct timeval timeout;
1881 * If there's no distinct data channel (such as if we're talking to an
1882 * older server), don't bother doing anything complicated. Just run the
1886 extract_files_child(ctl_fd, elist);
1890 if(pipe(child_pipe) == -1) {
1891 error("extract_list - error setting up pipe to extractor: %s\n",
1896 /* okay, ready to extract. fork a child to do the actual work */
1897 if ((pid = fork()) == 0) {
1898 /* this is the child process */
1899 /* never gets out of this clause */
1900 aclose(child_pipe[1]);
1901 extract_files_child(child_pipe[0], elist);
1905 /* This is the parent */
1907 error("writer_intermediary - error forking child");
1911 aclose(child_pipe[0]);
1913 if(data_fd > ctl_fd) max_fd = data_fd+1;
1914 else max_fd = ctl_fd+1;
1916 FD_SET(data_fd, &readset);
1917 FD_SET(ctl_fd, &readset);
1920 timeout.tv_sec = READ_TIMEOUT;
1921 timeout.tv_usec = 0;
1922 FD_COPY(&readset, &selectset);
1924 nfound = select(max_fd, &selectset, NULL, NULL,
1927 fprintf(stderr,"select error: %s\n", strerror(errno));
1931 if (nfound == 0) { /* timeout */
1932 fprintf(stderr, "timeout waiting %d seconds for restore\n",
1934 fprintf(stderr, "increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n");
1938 if(FD_ISSET(ctl_fd, &selectset)) {
1939 bytes_read = read(ctl_fd, buffer, sizeof(buffer)-1);
1940 switch(bytes_read) {
1942 if ((errno != EINTR) && (errno != EAGAIN)) {
1943 if (errno != EPIPE) {
1944 fprintf(stderr,"writer ctl fd read error: %s",
1947 FD_CLR(ctl_fd, &readset);
1952 FD_CLR(ctl_fd, &readset);
1956 char desired_tape[MAX_TAPE_LABEL_BUF];
1958 buffer[bytes_read] = '\0';
1959 /* if prompted for a tape, relay said prompt to the user */
1960 if(sscanf(buffer, "FEEDME %132s\n", desired_tape) == 1) {
1964 printf("Please insert tape %s. Continue? [Y|n]: ",
1968 input = agets(stdin); /* strips \n */
1969 if (strcasecmp("", input) == 0||
1970 strcasecmp("y", input) == 0||
1971 strcasecmp("yes", input) == 0) {
1972 send_to_tape_server(tape_control_sock, "OK");
1974 } else if (strcasecmp("n", input) == 0||
1975 strcasecmp("no", input) == 0) {
1976 send_to_tape_server(tape_control_sock, "ERROR");
1978 We are the middle process, so just die. */
1984 fprintf(stderr, "Strange message from tape server: %s", buffer);
1991 /* now read some dump data */
1992 if(FD_ISSET(data_fd, &selectset)) {
1993 bytes_read = read(data_fd, buffer, sizeof(buffer)-1);
1994 switch(bytes_read) {
1996 if ((errno != EINTR) && (errno != EAGAIN)) {
1997 if (errno != EPIPE) {
1998 fprintf(stderr,"writer data fd read error: %s",
2001 FD_CLR(data_fd, &readset);
2006 FD_CLR(data_fd, &readset);
2011 * spit what we got from the server to the child
2012 * process handling actual dumpfile extraction
2014 if(fullwrite(child_pipe[1], buffer, (size_t)bytes_read) < 0) {
2015 if(errno == EPIPE) {
2016 error("%s: pipe data reader has quit: %s\n",
2017 get_pname(), strerror(errno));
2020 error("Write error to extract child: %s\n",
2027 } while(FD_ISSET(ctl_fd, &readset) || FD_ISSET(data_fd, &readset));
2029 aclose(child_pipe[1]);
2031 waitpid(pid, &extractor_status, 0);
2032 if(WEXITSTATUS(extractor_status) != 0){
2033 int ret = WEXITSTATUS(extractor_status);
2034 if(ret == 255) ret = -1;
2035 error("Extractor child exited with status %d\n", ret);
2042 /* exec restore to do the actual restoration */
2044 /* does the actual extraction of files */
2046 * The original design had the dump image being returned exactly as it
2047 * appears on the tape, and this routine getting from the index server
2048 * whether or not it is compressed, on the assumption that the tape
2049 * server may not know how to uncompress it. But
2050 * - Amrestore can't do that. It returns either compressed or uncompressed
2051 * (always). Amrestore assumes it can uncompress files. It is thus a good
2052 * idea to run the tape server on a machine with gzip.
2053 * - The information about compression in the disklist is really only
2054 * for future dumps. It is possible to change compression on a drive
2055 * so the information in the disklist may not necessarily relate to
2056 * the dump image on the tape.
2057 * Consequently the design was changed to assuming that amrestore can
2058 * uncompress any dump image and have it return an uncompressed file
2064 EXTRACT_LIST *elist;
2066 amwait_t child_stat;
2071 tapelist_t *tlist = NULL;
2073 if (!is_extract_list_nonempty())
2075 printf("Extract list empty - No files to extract!\n");
2079 clean_extract_list();
2081 /* get tape device name from index server if none specified */
2082 if (tape_server_name == NULL) {
2083 tape_server_name = newstralloc(tape_server_name, server_name);
2085 if (tape_device_name == NULL) {
2086 if (send_command("TAPE") == -1)
2088 if (get_reply_line() == -1)
2091 if (!server_happy())
2096 /* skip reply number */
2097 tape_device_name = newstralloc(tape_device_name, l+4);
2100 if (strcmp(tape_device_name, "/dev/null") == 0)
2102 printf("amrecover: warning: using %s as the tape device will not work\n",
2107 for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist))
2108 if(elist->tape[0]!='/') {
2110 printf("\nExtracting files using tape drive %s on host %s.\n",
2111 tape_device_name, tape_server_name);
2112 printf("The following tapes are needed:");
2117 tlist = unmarshal_tapelist_str(elist->tape);
2118 for( ; tlist != NULL; tlist = tlist->next)
2119 printf(" %s", tlist->label);
2124 for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist))
2126 if(elist->tape[0]=='/') {
2128 printf("\nExtracting files from holding disk on host %s.\n",
2130 printf("The following files are needed:");
2135 tlist = unmarshal_tapelist_str(elist->tape);
2136 for( ; tlist != NULL; tlist = tlist->next)
2137 printf(" %s", tlist->label);
2144 if (getcwd(buf, sizeof(buf)) == NULL) {
2145 perror("extract_list: Current working directory unavailable");
2149 printf("Restoring files into directory %s\n", buf);
2151 if (samba_extract_method == SAMBA_SMBCLIENT)
2152 printf("(unless it is a Samba backup, that will go through to the SMB server)\n");
2154 if (!okay_to_continue(0,0,0))
2158 while ((elist = first_tape_list()) != NULL)
2160 if(elist->tape[0]=='/') {
2161 dump_device_name = newstralloc(dump_device_name, elist->tape);
2162 printf("Extracting from file ");
2163 tlist = unmarshal_tapelist_str(dump_device_name);
2164 for( ; tlist != NULL; tlist = tlist->next)
2165 printf(" %s", tlist->label);
2170 printf("Extracting files using tape drive %s on host %s.\n",
2171 tape_device_name, tape_server_name);
2172 tlist = unmarshal_tapelist_str(elist->tape);
2173 printf("Load tape %s now\n", tlist->label);
2175 otc = okay_to_continue(1,1,0);
2178 else if (otc == SKIP_TAPE) {
2179 delete_tape_list(elist); /* skip this tape */
2182 dump_device_name = newstralloc(dump_device_name, tape_device_name);
2184 dump_datestamp = newstralloc(dump_datestamp, elist->date);
2186 /* connect to the tape handler daemon on the tape drive server */
2187 if ((tape_control_sock = extract_files_setup(elist->tape, elist->fileno)) == -1)
2189 fprintf(stderr, "amrecover - can't talk to tape server\n");
2193 /* okay, ready to extract. fork a child to do the actual work */
2194 if ((pid = fork()) == 0)
2196 /* this is the child process */
2197 /* never gets out of this clause */
2198 writer_intermediary(tape_control_sock, tape_data_sock, elist);
2201 /* this is the parent */
2204 perror("extract_list - error forking child");
2205 aclose(tape_control_sock);
2209 /* store the child pid globally so that it can be killed on intr */
2210 extract_restore_child_pid = pid;
2212 /* wait for the child process to finish */
2213 if ((pid = waitpid(-1, &child_stat, 0)) == (pid_t)-1)
2215 perror("extract_list - error waiting for child");
2219 if(tape_data_sock != -1) {
2220 aclose(tape_data_sock);
2223 if (pid == extract_restore_child_pid)
2225 extract_restore_child_pid = -1;
2229 fprintf(stderr, "extract list - unknown child terminated?\n");
2232 if ((WIFEXITED(child_stat) != 0) && (WEXITSTATUS(child_stat) != 0))
2235 "extract_list - child returned non-zero status: %d\n",
2236 WEXITSTATUS(child_stat));
2237 otc = okay_to_continue(0,0,1);
2242 delete_tape_list(elist); /* tape failed so delete from list */
2246 delete_tape_list(elist); /* tape done so delete from list */