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.97 2006/03/09 16:51:41 martinea Exp $
29 * implements the "extract" command in amrecover
34 #include "amrecover.h"
35 #include "fileheader.h"
44 typedef struct EXTRACT_LIST_ITEM
48 struct EXTRACT_LIST_ITEM *next;
52 typedef struct EXTRACT_LIST
54 char *date; /* date tape created */
55 int level; /* level of dump */
56 char *tape; /* tape label */
57 int fileno; /* fileno on tape */
58 EXTRACT_LIST_ITEM *files; /* files to get off tape */
60 struct EXTRACT_LIST *next;
67 char *dump_device_name = NULL;
69 extern char *localhost;
71 /* global pid storage for interrupt handler */
72 pid_t extract_restore_child_pid = -1;
74 static EXTRACT_LIST *extract_list = NULL;
75 static int tape_control_sock = -1;
76 static int tape_data_sock = -1;
79 unsigned short samba_extract_method = SAMBA_TAR;
80 #endif /* SAMBA_CLIENT */
82 #define READ_TIMEOUT 240*60
84 static int okay_to_continue P((int, int, int));
85 void writer_intermediary P((int ctl_fd, int data_fd, EXTRACT_LIST *elist));
89 * Function: ssize_t read_buffer(datafd, buffer, buflen, timeout_s)
92 * read data from input file desciptor waiting up to timeout_s
93 * seconds before returning data.
96 * datafd - File descriptor to read from.
97 * buffer - Buffer to read into.
98 * buflen - Maximum number of bytes to read into buffer.
99 * timeout_s - Seconds to wait before returning what was already read.
102 * >0 - Number of data bytes in buffer.
104 * -1 - errno == ETIMEDOUT if no data available in specified time.
105 * errno == ENFILE if datafd is invalid.
106 * otherwise errno is set by select or read..
110 read_buffer(datafd, buffer, buflen, timeout_s)
117 struct timeval timeout;
122 if(datafd < 0 || datafd >= FD_SETSIZE) {
123 errno = EMFILE; /* out of range */
132 FD_SET(datafd, &readset);
133 timeout.tv_sec = timeout_s;
135 nfound = select(datafd+1, &readset, NULL, NULL, &timeout);
137 /* Select returned an error. */
138 fprintf(stderr,"select error: %s\n", strerror(errno));
144 /* Select timed out. */
145 if (timeout_s != 0) {
146 /* Not polling: a real read timeout */
147 fprintf(stderr,"timeout waiting for restore\n");
148 fprintf(stderr,"increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n");
155 if(!FD_ISSET(datafd, &readset))
158 /* Select says data is available, so read it. */
159 size = read(datafd, dataptr, spaceleft);
161 if ((errno == EINTR) || (errno == EAGAIN)) {
164 if (errno != EPIPE) {
165 fprintf(stderr, "read_buffer: read error - %s",
173 } while ((size > 0) && (spaceleft > 0));
175 return (((buflen-spaceleft) > 0) ? (buflen-spaceleft) : size);
179 EXTRACT_LIST *first_tape_list P((void))
184 EXTRACT_LIST *next_tape_list(list)
192 static void clear_tape_list(tape_list)
193 EXTRACT_LIST *tape_list;
195 EXTRACT_LIST_ITEM *this, *next;
198 this = tape_list->files;
206 tape_list->files = NULL;
210 /* remove a tape list from the extract list, clearing the tape list
211 beforehand if necessary */
212 void delete_tape_list(tape_list)
213 EXTRACT_LIST *tape_list;
215 EXTRACT_LIST *this, *prev;
217 if (tape_list == NULL)
220 /* is it first on the list? */
221 if (tape_list == extract_list)
223 extract_list = tape_list->next;
224 clear_tape_list(tape_list);
225 amfree(tape_list->date);
226 amfree(tape_list->tape);
231 /* so not first on list - find it and delete */
233 this = extract_list->next;
236 if (this == tape_list)
238 prev->next = tape_list->next;
239 clear_tape_list(tape_list);
240 amfree(tape_list->date);
241 amfree(tape_list->tape);
252 /* return the number of files on a tape's list */
253 int length_of_tape_list(tape_list)
254 EXTRACT_LIST *tape_list;
256 EXTRACT_LIST_ITEM *fn;
260 for (fn = tape_list->files; fn != NULL; fn = fn->next)
267 void clear_extract_list P((void))
269 while (extract_list != NULL)
270 delete_tape_list(extract_list);
274 /* returns -1 if error */
275 /* returns 0 on succes */
276 /* returns 1 if already added */
277 static int add_extract_item(ditem)
280 EXTRACT_LIST *this, *this1;
281 EXTRACT_LIST_ITEM *that, *curr;
282 char *ditem_path = NULL;
284 ditem_path = stralloc(ditem->path);
285 clean_pathname(ditem_path);
287 for (this = extract_list; this != NULL; this = this->next)
289 /* see if this is the list for the tape */
290 if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
292 /* yes, so add to list */
296 if (strcmp(curr->path,ditem_path) == 0) {
302 that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
303 that->path = stralloc(ditem_path);
304 that->next = this->files;
305 this->files = that; /* add at front since easiest */
311 /* so this is the first time we have seen this tape */
312 this = (EXTRACT_LIST *)alloc(sizeof(EXTRACT_LIST));
313 this->tape = stralloc(ditem->tape);
314 this->level = ditem->level;
315 this->fileno = ditem->fileno;
316 this->date = stralloc(ditem->date);
317 that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
318 that->path = stralloc(ditem_path);
322 /* add this in date increasing order */
323 /* because restore must be done in this order */
324 /* add at begining */
325 if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0)
327 this->next = extract_list;
332 for (this1 = extract_list; this1->next != NULL; this1 = this1->next)
334 /* add in the middle */
335 if(strcmp(this->date,this1->next->date) < 0)
337 this->next = this1->next;
351 /* returns -1 if error */
352 /* returns 0 on deletion */
353 /* returns 1 if not there */
354 static int delete_extract_item(ditem)
358 EXTRACT_LIST_ITEM *that, *prev;
359 char *ditem_path = NULL;
361 ditem_path = stralloc(ditem->path);
362 clean_pathname(ditem_path);
364 for (this = extract_list; this != NULL; this = this->next)
366 /* see if this is the list for the tape */
367 if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
369 /* yes, so find file on list */
371 if (strcmp(that->path, ditem_path) == 0)
374 this->files = that->next;
377 /* if list empty delete it */
378 if (this->files == NULL)
379 delete_tape_list(this);
387 if (strcmp(that->path, ditem_path) == 0)
389 prev->next = that->next;
415 regex = glob_to_regex(glob);
416 dbprintf(("add_glob (%s) -> %s\n", glob, regex));
417 if ((s = validate_regexp(regex)) != NULL) {
418 printf("\"%s\" is not a valid shell wildcard pattern: ", glob);
423 * glob_to_regex() anchors the beginning of the pattern with ^,
424 * but we will be tacking it onto the end of the current directory
425 * in add_file, so strip that off. Also, it anchors the end with
426 * $, but we need to match an optional trailing /, so tack that on
429 regex_path = stralloc(regex + 1);
431 regex_path[strlen(regex_path) - 1] = '\0';
432 strappend(regex_path, "[/]*$");
433 add_file(glob, regex_path);
437 void add_regex(regex)
442 if ((s = validate_regexp(regex)) != NULL) {
443 printf("\"%s\" is not a valid regular expression: ", regex);
447 add_file(regex, regex);
450 void add_file(path, regex)
454 DIR_ITEM *ditem, lditem;
455 char *path_on_disk = NULL;
456 char *path_on_disk_slash = NULL;
461 char *dir, *dir_undo, dir_undo_ch = '\0';
462 char *ditem_path = NULL;
469 if (disk_path == NULL) {
470 printf("Must select directory before adding files\n");
473 memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
475 dbprintf(("add_file: Looking for \"%s\"\n", regex));
477 /* remove "/" at end of path */
479 while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
481 /* convert path (assumed in cwd) to one on disk */
482 if (strcmp(disk_path, "/") == 0) {
484 if (strcmp(regex, "/[/]*$") == 0) {
485 /* We want '/' to match everything in directory... */
486 path_on_disk = stralloc("/[^/]*[/]*$");
488 /* No mods needed if already starts with '/' */
489 path_on_disk = stralloc(regex);
493 path_on_disk = stralloc2("/", regex);
496 char *clean_disk_path = clean_regex(disk_path);
497 path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
498 amfree(clean_disk_path);
501 path_on_disk_slash = stralloc2(path_on_disk, "/");
503 dbprintf(("add_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n",
504 regex, path_on_disk));
507 for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
509 dbprintf(("add_file: Pondering ditem->path=\"%s\"\n", ditem->path));
510 if (match(path_on_disk, ditem->path)
511 || match(path_on_disk_slash, ditem->path))
514 j = strlen(ditem->path);
515 if((j > 0 && ditem->path[j-1] == '/')
516 || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
517 { /* It is a directory */
519 ditem_path = newstralloc(ditem_path, ditem->path);
520 clean_pathname(ditem_path);
522 cmd = stralloc2("ORLD ", ditem_path);
523 if(send_command(cmd) == -1) {
526 amfree(path_on_disk);
527 amfree(path_on_disk_slash);
533 if ((i = get_reply_line()) == -1) {
535 amfree(path_on_disk);
536 amfree(path_on_disk_slash);
539 if(i==0) /* assume something wrong */
542 amfree(path_on_disk);
543 amfree(path_on_disk_slash);
550 lditem.path = newstralloc(lditem.path, ditem->path);
551 /* skip the last line -- duplicate of the preamble */
552 while ((i = get_reply_line()) != 0)
556 amfree(path_on_disk);
557 amfree(path_on_disk_slash);
562 if(dir_undo) *dir_undo = dir_undo_ch;
564 cmd = stralloc(l); /* save for error report */
566 continue; /* throw the rest of the lines away */
569 if (!server_happy()) {
574 if(strncmp(l, sc, sizeof(sc)-1) != 0) {
575 err = "bad reply: not 201-";
579 s = l + sizeof(sc)-1;
582 skip_whitespace(s, ch);
584 err = "bad reply: missing date field";
588 skip_non_whitespace(s, ch);
590 lditem.date = newstralloc(lditem.date, fp);
593 skip_whitespace(s, ch);
594 if(ch == '\0' || sscanf(s - 1, "%d", &lditem.level) != 1) {
595 err = "bad reply: cannot parse level field";
600 skip_whitespace(s, ch);
602 err = "bad reply: missing tape field";
606 skip_non_whitespace(s, ch);
608 lditem.tape = newstralloc(lditem.tape, fp);
611 if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
612 skip_whitespace(s, ch);
613 if(ch == '\0' || sscanf(s - 1, "%d", &lditem.fileno) != 1) {
614 err = "bad reply: cannot parse fileno field";
620 skip_whitespace(s, ch);
622 err = "bad reply: missing directory field";
626 skip_non_whitespace(s, ch);
628 dir_undo_ch = *dir_undo;
631 switch(add_extract_item(&lditem)) {
633 printf("System error\n");
634 dbprintf(("add_file: (Failed) System error\n"));
637 printf("Added dir %s at date %s\n",
638 ditem_path, lditem.date);
639 dbprintf(("add_file: (Successful) Added dir %s at date %s\n",
640 ditem_path,lditem.date));
647 if(!server_happy()) {
652 } else if(added == 0) {
653 printf("dir %s already added\n", ditem_path);
654 dbprintf(("add_file: dir %s already added\n", ditem_path));
657 else /* It is a file */
659 switch(add_extract_item(ditem)) {
661 printf("System error\n");
662 dbprintf(("add_file: (Failed) System error\n"));
665 printf("Added %s\n", ditem->path);
666 dbprintf(("add_file: (Successful) Added %s\n",
670 printf("File %s already added\n", ditem->path);
671 dbprintf(("add_file: file %s already added\n",
681 amfree(path_on_disk);
682 amfree(path_on_disk_slash);
685 printf("File %s doesn't exist in directory\n", path);
686 dbprintf(("add_file: (Failed) File %s doesn't exist in directory\n",
692 void delete_glob(glob)
699 regex = glob_to_regex(glob);
700 dbprintf(("delete_glob (%s) -> %s\n", glob, regex));
701 if ((s = validate_regexp(regex)) != NULL) {
702 printf("\"%s\" is not a valid shell wildcard pattern: ", glob);
707 * glob_to_regex() anchors the beginning of the pattern with ^,
708 * but we will be tacking it onto the end of the current directory
709 * in add_file, so strip that off. Also, it anchors the end with
710 * $, but we need to match an optional trailing /, so tack that on
713 regex_path = stralloc(regex + 1);
715 regex_path[strlen(regex_path) - 1] = '\0';
716 strappend(regex_path, "[/]*$");
717 delete_file(glob, regex_path);
721 void delete_regex(regex)
726 if ((s = validate_regexp(regex)) != NULL) {
727 printf("\"%s\" is not a valid regular expression: ", regex);
731 delete_file(regex, regex);
734 void delete_file(path, regex)
738 DIR_ITEM *ditem, lditem;
739 char *path_on_disk = NULL;
740 char *path_on_disk_slash = NULL;
745 char *date, *date_undo, date_undo_ch = '\0';
746 char *tape, *tape_undo, tape_undo_ch = '\0';
747 char *dir, *dir_undo, dir_undo_ch = '\0';
749 char *ditem_path = NULL;
756 if (disk_path == NULL) {
757 printf("Must select directory before deleting files\n");
760 memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
762 dbprintf(("delete_file: Looking for \"%s\"\n", path));
763 /* remove "/" at the end of the path */
765 while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
767 /* convert path (assumed in cwd) to one on disk */
768 if (strcmp(disk_path, "/") == 0)
769 path_on_disk = stralloc2("/", regex);
771 char *clean_disk_path = clean_regex(disk_path);
772 path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
773 amfree(clean_disk_path);
776 path_on_disk_slash = stralloc2(path_on_disk, "/");
778 dbprintf(("delete_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n",
779 regex, path_on_disk));
781 for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
783 dbprintf(("delete_file: Pondering ditem->path=\"%s\"\n", ditem->path));
784 if (match(path_on_disk, ditem->path)
785 || match(path_on_disk_slash, ditem->path))
788 j = strlen(ditem->path);
789 if((j > 0 && ditem->path[j-1] == '/')
790 || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
791 { /* It is a directory */
792 ditem_path = newstralloc(ditem_path, ditem->path);
793 clean_pathname(ditem_path);
795 cmd = stralloc2("ORLD ", ditem_path);
796 if(send_command(cmd) == -1) {
799 amfree(path_on_disk);
800 amfree(path_on_disk_slash);
805 if ((i = get_reply_line()) == -1) {
807 amfree(path_on_disk);
808 amfree(path_on_disk_slash);
811 if(i==0) /* assume something wrong */
814 amfree(path_on_disk);
815 amfree(path_on_disk_slash);
821 lditem.path = newstralloc(lditem.path, ditem->path);
823 date_undo = tape_undo = dir_undo = NULL;
824 /* skip the last line -- duplicate of the preamble */
825 while ((i = get_reply_line()) != 0)
829 amfree(path_on_disk);
830 amfree(path_on_disk_slash);
835 if(tape_undo) *tape_undo = tape_undo_ch;
836 if(dir_undo) *dir_undo = dir_undo_ch;
837 date_undo = tape_undo = dir_undo = NULL;
838 cmd = stralloc(l); /* save for the error report */
840 continue; /* throw the rest of the lines away */
843 if (!server_happy()) {
848 if(strncmp(l, sc, sizeof(sc)-1) != 0) {
849 err = "bad reply: not 201-";
852 s = l + sizeof(sc)-1;
855 skip_whitespace(s, ch);
857 err = "bad reply: missing date field";
861 skip_non_whitespace(s, ch);
863 date_undo_ch = *date_undo;
866 skip_whitespace(s, ch);
867 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
868 err = "bad reply: cannot parse level field";
873 skip_whitespace(s, ch);
875 err = "bad reply: missing tape field";
879 skip_non_whitespace(s, ch);
881 tape_undo_ch = *tape_undo;
884 if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
885 skip_whitespace(s, ch);
886 if(ch == '\0' || sscanf(s - 1, "%d", &fileno) != 1) {
887 err = "bad reply: cannot parse fileno field";
893 skip_whitespace(s, ch);
895 err = "bad reply: missing directory field";
899 skip_non_whitespace(s, ch);
901 dir_undo_ch = *dir_undo;
904 lditem.date = newstralloc(lditem.date, date);
906 lditem.tape = newstralloc(lditem.tape, tape);
907 switch(delete_extract_item(&lditem)) {
909 printf("System error\n");
910 dbprintf(("delete_file: (Failed) System error\n"));
913 printf("Deleted dir %s at date %s\n", ditem_path, date);
914 dbprintf(("delete_file: (Successful) Deleted dir %s at date %s\n",
922 if(!server_happy()) {
929 } else if(deleted == 0) {
930 printf("Warning - dir '%s' not on tape list\n",
932 dbprintf(("delete_file: dir '%s' not on tape list\n",
938 switch(delete_extract_item(ditem)) {
940 printf("System error\n");
941 dbprintf(("delete_file: (Failed) System error\n"));
944 printf("Deleted %s\n", ditem->path);
945 dbprintf(("delete_file: (Successful) Deleted %s\n",
949 printf("Warning - file '%s' not on tape list\n",
951 dbprintf(("delete_file: file '%s' not on tape list\n",
960 amfree(path_on_disk);
961 amfree(path_on_disk_slash);
964 printf("File %s doesn't exist in directory\n", path);
965 dbprintf(("delete_file: (Failed) File %s doesn't exist in directory\n",
971 /* print extract list into file. If NULL ptr passed print to screen */
972 void display_extract_list(file)
976 EXTRACT_LIST_ITEM *that;
983 if ((pager = getenv("PAGER")) == NULL)
988 * Set up the pager command so if the pager is terminated, we do
989 * not get a SIGPIPE back.
991 pager_command = stralloc2(pager, " ; /bin/cat > /dev/null");
992 if ((fp = popen(pager_command, "w")) == NULL)
994 printf("Warning - can't pipe through %s\n", pager);
997 amfree(pager_command);
1001 if ((fp = fopen(file, "w")) == NULL)
1003 printf("Can't open file '%s' to print extract list into\n", file);
1008 for (this = extract_list; this != NULL; this = this->next)
1010 fprintf(fp, "TAPE %s LEVEL %d DATE %s\n",
1011 this->tape, this->level, this->date);
1012 for (that = this->files; that != NULL; that = that->next)
1013 fprintf(fp, "\t%s\n", that->path);
1019 printf("Extract list written to file %s\n", file);
1025 /* returns 0 if extract list empty and 1 if it isn't */
1026 int is_extract_list_nonempty P((void))
1028 return (extract_list != NULL);
1032 /* prints continue prompt and waits for response,
1033 returns 0 if don't, non-0 if do */
1034 static int okay_to_continue(allow_tape, allow_skip, allow_retry)
1049 prompt = "New tape device [?]: ";
1050 } else if (allow_tape && allow_skip) {
1051 prompt = "Continue [?/Y/n/s/t]? ";
1052 } else if (allow_tape && !allow_skip) {
1053 prompt = "Continue [?/Y/n/t]? ";
1054 } else if (allow_retry) {
1055 prompt = "Continue [?/Y/n/r]? ";
1057 prompt = "Continue [?/Y/n]? ";
1059 fputs(prompt, stdout);
1060 fflush(stdout); fflush(stderr);
1062 if ((line = agets(stdin)) == NULL) {
1073 while ((ch = *s++) != '\0' && isspace(ch)) {}
1076 printf("Enter a new device ([host:]device) or \"default\"\n");
1078 printf("Enter \"y\"es to continue, \"n\"o to stop");
1080 printf(", \"s\"kip this tape");
1083 printf(" or \"r\"etry this tape");
1086 printf(" or \"t\"ape to change tape drives");
1090 } else if (get_tape) {
1093 } else if (ch == '\0' || ch == 'Y' || ch == 'y') {
1095 } else if (allow_tape && (ch == 'T' || ch == 't')) {
1097 } else if (ch == 'N' || ch == 'n') {
1099 } else if (allow_retry && (ch == 'R' || ch == 'r')) {
1101 } else if (allow_skip && (ch == 'S' || ch == 's')) {
1109 static void send_to_tape_server(tss, cmd)
1113 char *msg = stralloc2(cmd, "\r\n");
1115 if (fullwrite(tss, msg, strlen(msg)) < 0)
1117 error("Error writing to tape server");
1124 /* start up connection to tape server and set commands to initiate
1125 transfer of dump image.
1126 Return tape server socket on success, -1 on error. */
1127 static int extract_files_setup(label, fsf)
1132 int my_port, my_data_port;
1133 char *disk_regex = NULL;
1134 char *host_regex = NULL;
1135 char *service_name = NULL;
1137 char *clean_datestamp, *ch, *ch1;
1138 char *our_feature_string = NULL;
1141 service_name = stralloc2("amidxtape", SERVICE_SUFFIX);
1143 /* get tape server details */
1144 if ((sp = getservbyname(service_name, "tcp")) == NULL)
1146 printf("%s/tcp unknown protocol - config error?\n", service_name);
1147 amfree(service_name);
1150 amfree(service_name);
1151 seteuid(0); /* it either works ... */
1153 tape_control_sock = stream_client_privileged(tape_server_name,
1159 if (tape_control_sock < 0)
1161 printf("cannot connect to %s: %s\n", tape_server_name, strerror(errno));
1164 if (my_port >= IPPORT_RESERVED) {
1165 aclose(tape_control_sock);
1166 printf("did not get a reserved port: %d\n", my_port);
1171 seteuid(getuid()); /* put it back */
1173 /* do the security thing */
1174 line = get_security();
1175 send_to_tape_server(tape_control_sock, line);
1176 memset(line, '\0', strlen(line));
1179 disk_regex = alloc(strlen(disk_name) * 2 + 3);
1184 /* we want to force amrestore to only match disk_name exactly */
1187 /* We need to escape some characters first... NT compatibilty crap */
1188 for (; *ch != 0; ch++, ch1++) {
1189 switch (*ch) { /* done this way in case there are more */
1192 /* no break; we do want to fall through... */
1198 /* we want to force amrestore to only match disk_name exactly */
1203 host_regex = alloc(strlen(dump_hostname) * 2 + 3);
1208 /* we want to force amrestore to only match dump_hostname exactly */
1211 /* We need to escape some characters first... NT compatibilty crap */
1212 for (; *ch != 0; ch++, ch1++) {
1213 switch (*ch) { /* done this way in case there are more */
1216 /* no break; we do want to fall through... */
1222 /* we want to force amrestore to only match dump_hostname exactly */
1227 clean_datestamp = stralloc(dump_datestamp);
1228 for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) {
1236 /* push our feature list off to the tape server */
1237 /* XXX assumes that index server and tape server are equivalent, ew */
1238 if(am_has_feature(indexsrv_features, fe_amidxtaped_exchange_features)){
1239 char buffer[32768] = "\0";
1241 our_feature_string = am_feature_to_string(our_features);
1242 tt = newstralloc2(tt, "FEATURES=", our_feature_string);
1243 send_to_tape_server(tape_control_sock, tt);
1244 if (read(tape_control_sock, buffer, sizeof(buffer)) <= 0) {
1245 error("Could not read features from control socket\n");
1249 tapesrv_features = am_string_to_feature(buffer);
1250 amfree(our_feature_string);
1254 if(am_has_feature(indexsrv_features, fe_amidxtaped_header) &&
1255 am_has_feature(indexsrv_features, fe_amidxtaped_device) &&
1256 am_has_feature(indexsrv_features, fe_amidxtaped_host) &&
1257 am_has_feature(indexsrv_features, fe_amidxtaped_disk) &&
1258 am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) {
1260 if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) {
1261 tt = newstralloc2(tt, "CONFIG=", config);
1262 send_to_tape_server(tape_control_sock, tt);
1264 if(am_has_feature(indexsrv_features, fe_amidxtaped_label) &&
1265 label && label[0] != '/') {
1266 tt = newstralloc2(tt,"LABEL=",label);
1267 send_to_tape_server(tape_control_sock, tt);
1269 if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) {
1271 snprintf(v_fsf, 99, "%d", fsf);
1272 tt = newstralloc2(tt, "FSF=",v_fsf);
1273 send_to_tape_server(tape_control_sock, tt);
1275 send_to_tape_server(tape_control_sock, "HEADER");
1276 tt = newstralloc2(tt, "DEVICE=", dump_device_name);
1277 send_to_tape_server(tape_control_sock, tt);
1278 tt = newstralloc2(tt, "HOST=", host_regex);
1279 send_to_tape_server(tape_control_sock, tt);
1280 tt = newstralloc2(tt, "DISK=", disk_regex);
1281 send_to_tape_server(tape_control_sock, tt);
1282 tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp);
1283 send_to_tape_server(tape_control_sock, tt);
1284 send_to_tape_server(tape_control_sock, "END");
1287 else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) {
1288 /* send to the tape server what tape file we want */
1297 send_to_tape_server(tape_control_sock, "6");
1298 send_to_tape_server(tape_control_sock, "-h");
1299 send_to_tape_server(tape_control_sock, "-p");
1300 send_to_tape_server(tape_control_sock, dump_device_name);
1301 send_to_tape_server(tape_control_sock, host_regex);
1302 send_to_tape_server(tape_control_sock, disk_regex);
1303 send_to_tape_server(tape_control_sock, clean_datestamp);
1305 dbprintf(("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n",
1306 dump_device_name, host_regex, disk_regex, clean_datestamp));
1310 * split-restoring amidxtaped versions will expect to set up a data
1311 * connection for dumpfile data, distinct from the socket we're already
1312 * using for control data
1315 if(am_has_feature(tapesrv_features, fe_recover_splits)){
1320 nread = read(tape_control_sock, buffer, sizeof(buffer));
1323 error("Could not read from control socket: %s\n",
1328 buffer[nread] = '\0';
1329 if (sscanf(buffer, "CONNECT %d\n", &data_port) != 1) {
1330 error("Recieved invalid port number message from control socket: %s\n",
1335 tape_data_sock = stream_client_privileged(server_name,
1341 if(tape_data_sock == -1){
1342 error("Unable to make data connection to server: %s\n",
1347 amfree(our_feature_string);
1349 line = get_security();
1351 send_to_tape_server(tape_data_sock, line);
1352 memset(line, '\0', strlen(line));
1358 amfree(clean_datestamp);
1360 return tape_control_sock;
1364 void read_file_header(buffer, file, buflen, tapedev)
1370 * Reads the first block of a tape file.
1375 bytes_read = read_buffer(tapedev, buffer, buflen, READ_TIMEOUT);
1376 if(bytes_read < 0) {
1377 error("error reading header (%s), check amidxtaped.*.debug on server",
1382 if((size_t)bytes_read < buflen) {
1383 fprintf(stderr, "%s: short block %d byte%s\n",
1384 get_pname(), (int)bytes_read, (bytes_read == 1) ? "" : "s");
1385 print_header(stdout, file);
1386 error("Can't read file header");
1390 /* bytes_read == buflen */
1391 parse_file_header(buffer, file, bytes_read);
1394 enum dumptypes {IS_UNKNOWN, IS_DUMP, IS_GNUTAR, IS_TAR, IS_SAMBA, IS_SAMBA_TAR};
1396 static void extract_files_child(in_fd, elist)
1398 EXTRACT_LIST *elist;
1401 int extra_params = 0;
1403 char **restore_args = NULL;
1405 EXTRACT_LIST_ITEM *fn;
1406 enum dumptypes dumptype = IS_UNKNOWN;
1407 char buffer[DISK_BLOCK_BYTES];
1411 int passwd_field = -1;
1413 char *domain = NULL, *smbpass = NULL;
1416 /* code executed by child to do extraction */
1419 /* make in_fd be our stdin */
1420 if (dup2(in_fd, STDIN_FILENO) == -1)
1422 error("dup2 failed in extract_files_child: %s", strerror(errno));
1426 /* read the file header */
1428 read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO);
1430 if(file.type != F_DUMPFILE) {
1431 print_header(stdout, &file);
1432 error("bad header");
1436 if (file.program != NULL) {
1438 if (strcmp(file.program, GNUTAR) == 0)
1439 dumptype = IS_GNUTAR;
1442 if (dumptype == IS_UNKNOWN) {
1443 len_program = strlen(file.program);
1444 if(len_program >= 3 &&
1445 strcmp(&file.program[len_program-3],"tar") == 0)
1450 if (dumptype == IS_UNKNOWN && strcmp(file.program, SAMBA_CLIENT) ==0) {
1451 if (samba_extract_method == SAMBA_TAR)
1452 dumptype = IS_SAMBA_TAR;
1454 dumptype = IS_SAMBA;
1459 /* form the arguments to restore */
1460 files_off_tape = length_of_tape_list(elist);
1479 #if defined(XFSDUMP)
1480 if (strcmp(file.program, XFSDUMP) == 0) {
1481 extra_params = 4 + files_off_tape;
1491 restore_args = (char **)alloc((extra_params + files_off_tape + 1)
1496 restore_args[j++] = stralloc("smbclient");
1497 smbpass = findpass(file.disk, &domain);
1499 restore_args[j++] = stralloc(file.disk);
1501 restore_args[j++] = stralloc("-U");
1502 restore_args[j++] = smbpass;
1504 restore_args[j++] = stralloc("-W");
1505 restore_args[j++] = stralloc(domain);
1510 restore_args[j++] = stralloc("-d0");
1511 restore_args[j++] = stralloc("-Tx");
1512 restore_args[j++] = stralloc("-"); /* data on stdin */
1517 restore_args[j++] = stralloc("tar");
1518 restore_args[j++] = stralloc("--numeric-owner");
1519 restore_args[j++] = stralloc("-xpGvf");
1520 restore_args[j++] = stralloc("-"); /* data on stdin */
1523 restore_args[j++] = stralloc("tar");
1524 restore_args[j++] = stralloc("-xpvf");
1525 restore_args[j++] = stralloc("-"); /* data on stdin */
1529 restore_args[j++] = stralloc("restore");
1531 restore_args[j++] = stralloc("-xB");
1533 #if defined(XFSDUMP)
1534 if (strcmp(file.program, XFSDUMP) == 0) {
1535 restore_args[j++] = stralloc("-v");
1536 restore_args[j++] = stralloc("silent");
1540 if (strcmp(file.program, VDUMP) == 0) {
1541 restore_args[j++] = stralloc("xf");
1542 restore_args[j++] = stralloc("-"); /* data on stdin */
1546 restore_args[j++] = stralloc("xbf");
1547 restore_args[j++] = stralloc("2"); /* read in units of 1K */
1548 restore_args[j++] = stralloc("-"); /* data on stdin */
1553 for (i = 0, fn = elist->files; i < files_off_tape; i++, fn = fn->next)
1560 if (strcmp(fn->path, "/") == 0)
1561 restore_args[j++] = stralloc(".");
1563 restore_args[j++] = stralloc2(".", fn->path);
1567 #if defined(XFSDUMP)
1568 if (strcmp(file.program, XFSDUMP) == 0) {
1570 * xfsrestore needs a -s option before each file to be
1571 * restored, and also wants them to be relative paths.
1573 restore_args[j++] = stralloc("-s");
1574 restore_args[j++] = stralloc(fn->path + 1);
1578 restore_args[j++] = stralloc(fn->path);
1582 #if defined(XFSDUMP)
1583 if (strcmp(file.program, XFSDUMP) == 0) {
1584 restore_args[j++] = stralloc("-");
1585 restore_args[j++] = stralloc(".");
1588 restore_args[j] = NULL;
1593 cmd = stralloc(SAMBA_CLIENT);
1596 /* fall through to ... */
1602 fprintf(stderr, "warning: GNUTAR program not available.\n");
1603 cmd = stralloc("tar");
1605 cmd = stralloc(GNUTAR);
1612 if (strcmp(file.program, DUMP) == 0) {
1613 cmd = stralloc(RESTORE);
1617 if (strcmp(file.program, VDUMP) == 0) {
1618 cmd = stralloc(VRESTORE);
1622 if (strcmp(file.program, VXDUMP) == 0) {
1623 cmd = stralloc(VXRESTORE);
1626 #if defined(XFSDUMP)
1627 if (strcmp(file.program, XFSDUMP) == 0) {
1628 cmd = stralloc(XFSRESTORE);
1632 fprintf(stderr, "warning: restore program for %s not available.\n",
1634 cmd = stralloc("restore");
1638 dbprintf(("Exec'ing %s with arguments:\n", cmd));
1639 for (i = 0; i < j; i++) {
1640 if( i == passwd_field)
1641 dbprintf(("\tXXXXX\n"));
1643 dbprintf(("\t%s\n", restore_args[i]));
1645 (void)execv(cmd, restore_args);
1646 /* only get here if exec failed */
1648 for (i = 0; i < j; i++) {
1649 amfree(restore_args[i]);
1651 amfree(restore_args);
1653 perror("amrecover couldn't exec");
1654 fprintf(stderr, " problem executing %s\n", cmd);
1662 * Interpose something between the process writing out the dump (writing it to
1663 * some extraction program, really) and the socket from which we're reading, so
1664 * that we can do things like prompt for human interaction for multiple tapes.
1666 void writer_intermediary(ctl_fd, data_fd, elist)
1667 int ctl_fd, data_fd;
1668 EXTRACT_LIST *elist;
1672 char buffer[DISK_BLOCK_BYTES];
1675 amwait_t extractor_status;
1677 fd_set readset, selectset;
1678 struct timeval timeout;
1681 * If there's no distinct data channel (such as if we're talking to an
1682 * older server), don't bother doing anything complicated. Just run the
1686 extract_files_child(ctl_fd, elist);
1690 if(pipe(child_pipe) == -1) {
1691 error("extract_list - error setting up pipe to extractor: %s\n",
1696 /* okay, ready to extract. fork a child to do the actual work */
1697 if ((pid = fork()) == 0) {
1698 /* this is the child process */
1699 /* never gets out of this clause */
1700 aclose(child_pipe[1]);
1701 extract_files_child(child_pipe[0], elist);
1705 /* This is the parent */
1707 error("writer_intermediary - error forking child");
1711 aclose(child_pipe[0]);
1713 if(data_fd > ctl_fd) max_fd = data_fd+1;
1714 else max_fd = ctl_fd+1;
1716 FD_SET(data_fd, &readset);
1717 FD_SET(ctl_fd, &readset);
1720 timeout.tv_sec = READ_TIMEOUT;
1721 timeout.tv_usec = 0;
1722 FD_COPY(&readset, &selectset);
1724 nfound = select(max_fd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL,
1727 fprintf(stderr,"select error: %s\n", strerror(errno));
1731 if (nfound == 0) { /* timeout */
1732 fprintf(stderr, "timeout waiting %d seconds for restore\n",
1734 fprintf(stderr, "increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n");
1738 if(FD_ISSET(ctl_fd, &selectset)) {
1739 bytes_read = read(ctl_fd, buffer, sizeof(buffer)-1);
1740 switch(bytes_read) {
1742 if ((errno != EINTR) && (errno != EAGAIN)) {
1743 if (errno != EPIPE) {
1744 fprintf(stderr,"writer ctl fd read error: %s",
1747 FD_CLR(ctl_fd, &readset);
1752 FD_CLR(ctl_fd, &readset);
1756 char desired_tape[MAX_TAPE_LABEL_BUF];
1758 buffer[bytes_read] = '\0';
1759 /* if prompted for a tape, relay said prompt to the user */
1760 if(sscanf(buffer, "FEEDME %s\n", desired_tape) == 1) {
1764 printf("Please insert tape %s. Continue? [Y|n]: ",
1768 input = agets(stdin); /* strips \n */
1769 if (strcasecmp("", input) == 0||
1770 strcasecmp("y", input) == 0||
1771 strcasecmp("yes", input) == 0) {
1772 send_to_tape_server(tape_control_sock, "OK");
1774 } else if (strcasecmp("n", input) == 0||
1775 strcasecmp("no", input) == 0) {
1776 send_to_tape_server(tape_control_sock, "ERROR");
1778 We are the middle process, so just die. */
1784 fprintf(stderr, "Strange message from tape server: %s", buffer);
1792 /* now read some dump data */
1793 if(FD_ISSET(data_fd, &selectset)) {
1794 bytes_read = read(data_fd, buffer, sizeof(buffer)-1);
1795 switch(bytes_read) {
1797 if ((errno != EINTR) && (errno != EAGAIN)) {
1798 if (errno != EPIPE) {
1799 fprintf(stderr,"writer data fd read error: %s",
1802 FD_CLR(data_fd, &readset);
1807 FD_CLR(data_fd, &readset);
1812 * spit what we got from the server to the child
1813 * process handling actual dumpfile extraction
1815 if((s = fullwrite(child_pipe[1], buffer, bytes_read)) < 0){
1816 if(errno == EPIPE) {
1817 error("%s: pipe data reader has quit: %s\n",
1818 get_pname(), strerror(errno));
1821 error("Write error to extract child: %s\n",
1828 } while(FD_ISSET(ctl_fd, &readset) || FD_ISSET(data_fd, &readset));
1830 aclose(child_pipe[1]);
1832 waitpid(pid, &extractor_status, 0);
1833 if(WEXITSTATUS(extractor_status) != 0){
1834 int ret = WEXITSTATUS(extractor_status);
1835 if(ret == 255) ret = -1;
1836 error("Extractor child exited with status %d\n", ret);
1843 /* exec restore to do the actual restoration */
1845 /* does the actual extraction of files */
1846 /* The original design had the dump image being returned exactly as it
1847 appears on the tape, and this routine getting from the index server
1848 whether or not it is compressed, on the assumption that the tape
1849 server may not know how to uncompress it. But
1850 - Amrestore can't do that. It returns either compressed or uncompressed
1851 (always). Amrestore assumes it can uncompress files. It is thus a good
1852 idea to run the tape server on a machine with gzip.
1853 - The information about compression in the disklist is really only
1854 for future dumps. It is possible to change compression on a drive
1855 so the information in the disklist may not necessarily relate to
1856 the dump image on the tape.
1857 Consequently the design was changed to assuming that amrestore can
1858 uncompress any dump image and have it return an uncompressed file
1860 void extract_files P((void))
1862 EXTRACT_LIST *elist;
1864 amwait_t child_stat;
1869 tapelist_t *tlist = NULL;
1871 if (!is_extract_list_nonempty())
1873 printf("Extract list empty - No files to extract!\n");
1877 /* get tape device name from index server if none specified */
1878 if (tape_server_name == NULL) {
1879 tape_server_name = newstralloc(tape_server_name, server_name);
1881 if (tape_device_name == NULL) {
1882 if (send_command("TAPE") == -1)
1884 if (get_reply_line() == -1)
1887 if (!server_happy())
1892 /* skip reply number */
1893 tape_device_name = newstralloc(tape_device_name, l+4);
1896 if (strcmp(tape_device_name, "/dev/null") == 0)
1898 printf("amrecover: warning: using %s as the tape device will not work\n",
1903 for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist))
1904 if(elist->tape[0]!='/') {
1906 printf("\nExtracting files using tape drive %s on host %s.\n",
1907 tape_device_name, tape_server_name);
1908 printf("The following tapes are needed:");
1913 tlist = unmarshal_tapelist_str(elist->tape);
1914 for( ; tlist != NULL; tlist = tlist->next)
1915 printf(" %s", tlist->label);
1920 for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist))
1921 if(elist->tape[0]=='/') {
1923 printf("\nExtracting files from holding disk on host %s.\n",
1925 printf("The following files are needed:");
1930 tlist = unmarshal_tapelist_str(elist->tape);
1931 for( ; tlist != NULL; tlist = tlist->next)
1932 printf(" %s", tlist->label);
1937 getcwd(buf, sizeof(buf));
1938 printf("Restoring files into directory %s\n", buf);
1940 if (samba_extract_method == SAMBA_SMBCLIENT)
1941 printf("(unless it is a Samba backup, that will go through to the SMB server)\n");
1943 if (!okay_to_continue(0,0,0))
1947 while ((elist = first_tape_list()) != NULL)
1949 if(elist->tape[0]=='/') {
1950 dump_device_name = newstralloc(dump_device_name, elist->tape);
1951 printf("Extracting from file ");
1952 tlist = unmarshal_tapelist_str(dump_device_name);
1953 for( ; tlist != NULL; tlist = tlist->next)
1954 printf(" %s", tlist->label);
1959 printf("Extracting files using tape drive %s on host %s.\n",
1960 tape_device_name, tape_server_name);
1961 tlist = unmarshal_tapelist_str(elist->tape);
1962 printf("Load tape %s now\n", tlist->label);
1964 otc = okay_to_continue(1,1,0);
1967 else if (otc == SKIP_TAPE) {
1968 delete_tape_list(elist); /* skip this tape */
1971 dump_device_name = newstralloc(dump_device_name, tape_device_name);
1973 dump_datestamp = newstralloc(dump_datestamp, elist->date);
1975 /* connect to the tape handler daemon on the tape drive server */
1976 if ((tape_control_sock = extract_files_setup(elist->tape, elist->fileno)) == -1)
1978 fprintf(stderr, "amrecover - can't talk to tape server\n");
1982 /* okay, ready to extract. fork a child to do the actual work */
1983 if ((pid = fork()) == 0)
1985 /* this is the child process */
1986 /* never gets out of this clause */
1987 writer_intermediary(tape_control_sock, tape_data_sock, elist);
1990 /* this is the parent */
1993 perror("extract_list - error forking child");
1997 /* store the child pid globally so that it can be killed on intr */
1998 extract_restore_child_pid = pid;
2000 /* wait for the child process to finish */
2001 if ((pid = waitpid(-1, &child_stat, 0)) == (pid_t)-1)
2003 perror("extract_list - error waiting for child");
2007 if(tape_data_sock != -1) {
2008 aclose(tape_data_sock);
2011 if (pid == extract_restore_child_pid)
2013 extract_restore_child_pid = -1;
2017 fprintf(stderr, "extract list - unknown child terminated?\n");
2020 if ((WIFEXITED(child_stat) != 0) && (WEXITSTATUS(child_stat) != 0))
2023 "extract_list - child returned non-zero status: %d\n",
2024 WEXITSTATUS(child_stat));
2025 otc = okay_to_continue(0,0,1);
2029 delete_tape_list(elist); /* tape failed so delete from list */
2031 else { /* RETRY_TAPE */
2035 delete_tape_list(elist); /* tape done so delete from list */