X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=recover-src%2Fextract_list.c;h=baac50775b4705014b229ac80b813ffb2a5f6e05;hb=b116e9366c7b2ea2c2eb53b0a13df4090e176235;hp=72b37c52bc0a86ced1762a1d084ff68b5b1c94e1;hpb=1194fb66aa28d9929c3f2bef3cc6c1c3f40a60a4;p=debian%2Famanda diff --git a/recover-src/extract_list.c b/recover-src/extract_list.c index 72b37c5..baac507 100644 --- a/recover-src/extract_list.c +++ b/recover-src/extract_list.c @@ -24,13 +24,13 @@ * file named AUTHORS, in the root directory of this distribution. */ /* - * $Id: extract_list.c,v 1.97 2006/03/09 16:51:41 martinea Exp $ + * $Id$ * * implements the "extract" command in amrecover */ #include "amanda.h" -#include "version.h" +#include "match.h" #include "amrecover.h" #include "fileheader.h" #include "dgram.h" @@ -40,50 +40,114 @@ #include "findpass.h" #endif #include "util.h" +#include "conffile.h" +#include "protocol.h" +#include "event.h" +#include "client_util.h" +#include "security.h" -typedef struct EXTRACT_LIST_ITEM -{ +typedef struct EXTRACT_LIST_ITEM { char *path; - struct EXTRACT_LIST_ITEM *next; } EXTRACT_LIST_ITEM; -typedef struct EXTRACT_LIST -{ +typedef struct EXTRACT_LIST { char *date; /* date tape created */ - int level; /* level of dump */ + int level; /* level of dump */ char *tape; /* tape label */ - int fileno; /* fileno on tape */ - EXTRACT_LIST_ITEM *files; /* files to get off tape */ + off_t fileno; /* fileno on tape */ + EXTRACT_LIST_ITEM *files; /* files to get off tape */ struct EXTRACT_LIST *next; } EXTRACT_LIST; +typedef struct ctl_data_s { + int header_done; + int child_pipe[2]; + int pid; + EXTRACT_LIST *elist; + dumpfile_t file; + data_path_t data_path; + char *addrs; + backup_support_option_t *bsu; + gint64 bytes_read; +} ctl_data_t; + #define SKIP_TAPE 2 #define RETRY_TAPE 3 -char *dump_device_name = NULL; - +static struct { + const char *name; + security_stream_t *fd; +} amidxtaped_streams[] = { +#define CTLFD 0 + { "CTL", NULL }, +#define DATAFD 1 + { "DATA", NULL }, +}; +#define NSTREAMS (int)(sizeof(amidxtaped_streams) / sizeof(amidxtaped_streams[0])) + + +static void amidxtaped_response(void *, pkt_t *, security_handle_t *); +static void stop_amidxtaped(void); +static char *dump_device_name = NULL; +static char *errstr; +static char *amidxtaped_line = NULL; extern char *localhost; /* global pid storage for interrupt handler */ pid_t extract_restore_child_pid = -1; static EXTRACT_LIST *extract_list = NULL; -static int tape_control_sock = -1; -static int tape_data_sock = -1; +static const security_driver_t *amidxtaped_secdrv; -#ifdef SAMBA_CLIENT unsigned short samba_extract_method = SAMBA_TAR; -#endif /* SAMBA_CLIENT */ #define READ_TIMEOUT 240*60 -static int okay_to_continue P((int, int, int)); -void writer_intermediary P((int ctl_fd, int data_fd, EXTRACT_LIST *elist)); - +EXTRACT_LIST *first_tape_list(void); +EXTRACT_LIST *next_tape_list(EXTRACT_LIST *list); +static int is_empty_dir(char *fname); +int is_extract_list_nonempty(void); +int length_of_tape_list(EXTRACT_LIST *tape_list); +void add_file(char *path, char *regex); +void add_glob(char *glob); +void add_regex(char *regex); +void clear_extract_list(void); +void clean_tape_list(EXTRACT_LIST *tape_list); +void clean_extract_list(void); +void check_file_overwrite(char *filename); +void delete_file(char *path, char *regex); +void delete_glob(char *glob); +void delete_regex(char *regex); +void delete_tape_list(EXTRACT_LIST *tape_list); +void display_extract_list(char *file); +void extract_files(void); +void read_file_header(char *buffer, + dumpfile_t *file, + size_t buflen, + int tapedev); +static int add_extract_item(DIR_ITEM *ditem); +static int delete_extract_item(DIR_ITEM *ditem); +static int extract_files_setup(char *label, off_t fsf); +static int okay_to_continue(int allow_tape, + int allow_skip, + int allow_retry); +static ssize_t read_buffer(int datafd, + char *buffer, + size_t buflen, + long timeout_s); +static void clear_tape_list(EXTRACT_LIST *tape_list); +static void extract_files_child(ctl_data_t *ctl_data); +static void send_to_tape_server(security_stream_t *stream, char *cmd); +int writer_intermediary(EXTRACT_LIST *elist); +int get_amidxtaped_line(void); +static void read_amidxtaped_data(void *, void *, ssize_t); +static char *merge_path(char *path1, char *path2); +static gboolean ask_file_overwrite(ctl_data_t *ctl_data); +static void start_processing_data(ctl_data_t *ctl_data); /* * Function: ssize_t read_buffer(datafd, buffer, buflen, timeout_s) @@ -107,25 +171,26 @@ void writer_intermediary P((int ctl_fd, int data_fd, EXTRACT_LIST *elist)); */ static ssize_t -read_buffer(datafd, buffer, buflen, timeout_s) -int datafd; -char *buffer; -size_t buflen; +read_buffer( + int datafd, + char * buffer, + size_t buflen, + long timeout_s) { ssize_t size = 0; - fd_set readset; + SELECT_ARG_TYPE readset; struct timeval timeout; char *dataptr; - size_t spaceleft; + ssize_t spaceleft; int nfound; - if(datafd < 0 || datafd >= FD_SETSIZE) { + if(datafd < 0 || datafd >= (int)FD_SETSIZE) { errno = EMFILE; /* out of range */ return -1; } dataptr = buffer; - spaceleft = buflen; + spaceleft = (ssize_t)buflen; do { FD_ZERO(&readset); @@ -135,7 +200,7 @@ size_t buflen; nfound = select(datafd+1, &readset, NULL, NULL, &timeout); if(nfound < 0 ) { /* Select returned an error. */ - fprintf(stderr,"select error: %s\n", strerror(errno)); + g_fprintf(stderr,_("select error: %s\n"), strerror(errno)); size = -1; break; } @@ -144,8 +209,8 @@ size_t buflen; /* Select timed out. */ if (timeout_s != 0) { /* Not polling: a real read timeout */ - fprintf(stderr,"timeout waiting for restore\n"); - fprintf(stderr,"increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n"); + g_fprintf(stderr,_("timeout waiting for restore\n")); + g_fprintf(stderr,_("increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n")); } errno = ETIMEDOUT; size = -1; @@ -156,13 +221,13 @@ size_t buflen; continue; /* Select says data is available, so read it. */ - size = read(datafd, dataptr, spaceleft); + size = read(datafd, dataptr, (size_t)spaceleft); if (size < 0) { if ((errno == EINTR) || (errno == EAGAIN)) { continue; } if (errno != EPIPE) { - fprintf(stderr, "read_buffer: read error - %s", + g_fprintf(stderr, _("read_buffer: read error - %s"), strerror(errno)); break; } @@ -172,28 +237,31 @@ size_t buflen; dataptr += size; } while ((size > 0) && (spaceleft > 0)); - return (((buflen-spaceleft) > 0) ? (buflen-spaceleft) : size); + return ((((ssize_t)buflen-spaceleft) > 0) ? ((ssize_t)buflen-spaceleft) : size); } -EXTRACT_LIST *first_tape_list P((void)) +EXTRACT_LIST * +first_tape_list(void) { return extract_list; } -EXTRACT_LIST *next_tape_list(list) -EXTRACT_LIST *list; +EXTRACT_LIST * +next_tape_list( + /*@keep@*/EXTRACT_LIST *list) { if (list == NULL) return NULL; return list->next; } -static void clear_tape_list(tape_list) -EXTRACT_LIST *tape_list; +static void +clear_tape_list( + EXTRACT_LIST * tape_list) { EXTRACT_LIST_ITEM *this, *next; - + this = tape_list->files; while (this != NULL) @@ -209,8 +277,9 @@ EXTRACT_LIST *tape_list; /* remove a tape list from the extract list, clearing the tape list beforehand if necessary */ -void delete_tape_list(tape_list) -EXTRACT_LIST *tape_list; +void +delete_tape_list( + EXTRACT_LIST *tape_list) { EXTRACT_LIST *this, *prev; @@ -250,8 +319,9 @@ EXTRACT_LIST *tape_list; /* return the number of files on a tape's list */ -int length_of_tape_list(tape_list) -EXTRACT_LIST *tape_list; +int +length_of_tape_list( + EXTRACT_LIST *tape_list) { EXTRACT_LIST_ITEM *fn; int n; @@ -264,18 +334,248 @@ EXTRACT_LIST *tape_list; } -void clear_extract_list P((void)) +void +clear_extract_list(void) { while (extract_list != NULL) delete_tape_list(extract_list); } +void +clean_tape_list( + EXTRACT_LIST *tape_list) +{ + EXTRACT_LIST_ITEM *fn1, *pfn1, *ofn1; + EXTRACT_LIST_ITEM *fn2, *pfn2, *ofn2; + int remove_fn1; + int remove_fn2; + + pfn1 = NULL; + fn1 = tape_list->files; + while (fn1 != NULL) { + remove_fn1 = 0; + + pfn2 = fn1; + fn2 = fn1->next; + while (fn2 != NULL && remove_fn1 == 0) { + remove_fn2 = 0; + if(strcmp(fn1->path, fn2->path) == 0) { + remove_fn2 = 1; + } else if (strncmp(fn1->path, fn2->path, strlen(fn1->path)) == 0 && + ((strlen(fn2->path) > strlen(fn1->path) && + fn2->path[strlen(fn1->path)] == '/') || + (fn1->path[strlen(fn1->path)-1] == '/'))) { + remove_fn2 = 1; + } else if (strncmp(fn2->path, fn1->path, strlen(fn2->path)) == 0 && + ((strlen(fn1->path) > strlen(fn2->path) && + fn1->path[strlen(fn2->path)] == '/') || + (fn2->path[strlen(fn2->path)-1] == '/'))) { + remove_fn1 = 1; + break; + } + + if (remove_fn2) { + dbprintf(_("removing path %s, it is included in %s\n"), + fn2->path, fn1->path); + ofn2 = fn2; + fn2 = fn2->next; + amfree(ofn2->path); + amfree(ofn2); + pfn2->next = fn2; + } else if (remove_fn1 == 0) { + pfn2 = fn2; + fn2 = fn2->next; + } + } + + if(remove_fn1 != 0) { + /* fn2->path is always valid */ + /*@i@*/ dbprintf(_("removing path %s, it is included in %s\n"), + /*@i@*/ fn1->path, fn2->path); + ofn1 = fn1; + fn1 = fn1->next; + amfree(ofn1->path); + if(pfn1 == NULL) { + amfree(tape_list->files); + tape_list->files = fn1; + } else { + amfree(pfn1->next); + pfn1->next = fn1; + } + } else { + pfn1 = fn1; + fn1 = fn1->next; + } + } +} + + +static char * +file_of_path( + char *path, + char **dir) +{ + char *npath = g_path_get_basename(path); + *dir = g_path_get_dirname(path); + if (strcmp(*dir, ".") == 0) { + amfree(*dir); + } + return npath; +} + +void +clean_extract_list(void) +{ + EXTRACT_LIST *this; + + for (this = extract_list; this != NULL; this = this->next) + clean_tape_list(this); +} + + +int add_to_unlink_list(char *path); +int do_unlink_list(void); +void free_unlink_list(void); + +typedef struct s_unlink_list { + char *path; + struct s_unlink_list *next; +} t_unlink_list; +t_unlink_list *unlink_list = NULL; + +int +add_to_unlink_list( + char *path) +{ + t_unlink_list *ul; + + if (!unlink_list) { + unlink_list = alloc(SIZEOF(*unlink_list)); + unlink_list->path = stralloc(path); + unlink_list->next = NULL; + } else { + for (ul = unlink_list; ul != NULL; ul = ul->next) { + if (strcmp(ul->path, path) == 0) + return 0; + } + ul = alloc(SIZEOF(*ul)); + ul->path = stralloc(path); + ul->next = unlink_list; + unlink_list = ul; + } + return 1; +} + +int +do_unlink_list(void) +{ + t_unlink_list *ul; + int ret = 1; + + for (ul = unlink_list; ul != NULL; ul = ul->next) { + if (unlink(ul->path) < 0) { + g_fprintf(stderr,_("Can't unlink %s: %s\n"), ul->path, strerror(errno)); + ret = 0; + } + } + return ret; +} + + +void +free_unlink_list(void) +{ + t_unlink_list *ul, *ul1; + + for (ul = unlink_list; ul != NULL; ul = ul1) { + amfree(ul->path); + ul1 = ul->next; + amfree(ul); + } + + unlink_list = NULL; +} + + + +void +check_file_overwrite( + char *dir) +{ + EXTRACT_LIST *this; + EXTRACT_LIST_ITEM *fn; + struct stat stat_buf; + char *filename; + char *path, *s; + + for (this = extract_list; this != NULL; this = this->next) { + for (fn = this->files; fn != NULL ; fn = fn->next) { + + /* Check path component of fn->path */ + + path = stralloc2(dir, fn->path); + if (path[strlen(path)-1] == '/') { + path[strlen(path)-1] = '\0'; + } + + s = path + strlen(dir) + 1; + while((s = strchr(s, '/'))) { + *s = '\0'; + if (lstat(path, &stat_buf) == 0) { + if(!S_ISDIR(stat_buf.st_mode)) { + if (add_to_unlink_list(path)) { + g_printf(_("WARNING: %s is not a directory, " + "it will be deleted.\n"), + path); + } + } + } + else if (errno != ENOENT) { + g_printf(_("Can't stat %s: %s\n"), path, strerror(errno)); + } + *s = '/'; + s++; + } + amfree(path); + + /* Check fn->path */ + + filename = stralloc2(dir, fn->path); + if (filename[strlen(filename)-1] == '/') { + filename[strlen(filename)-1] = '\0'; + } + + if (lstat(filename, &stat_buf) == 0) { + if(S_ISDIR(stat_buf.st_mode)) { + if(!is_empty_dir(filename)) { + g_printf(_("WARNING: All existing files in %s " + "will be deleted.\n"), filename); + } + } else if(S_ISREG(stat_buf.st_mode)) { + g_printf(_("WARNING: Existing file %s will be overwritten\n"), + filename); + } else { + if (add_to_unlink_list(filename)) { + g_printf(_("WARNING: Existing entry %s will be deleted\n"), + filename); + } + } + } else if (errno != ENOENT) { + g_printf(_("Can't stat %s: %s\n"), filename, strerror(errno)); + } + amfree(filename); + } + } +} + + /* returns -1 if error */ /* returns 0 on succes */ /* returns 1 if already added */ -static int add_extract_item(ditem) -DIR_ITEM *ditem; +static int +add_extract_item( + DIR_ITEM *ditem) { EXTRACT_LIST *this, *this1; EXTRACT_LIST_ITEM *that, *curr; @@ -322,7 +622,7 @@ DIR_ITEM *ditem; /* add this in date increasing order */ /* because restore must be done in this order */ /* add at begining */ - if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0) + if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0) { this->next = extract_list; extract_list = this; @@ -351,8 +651,9 @@ DIR_ITEM *ditem; /* returns -1 if error */ /* returns 0 on deletion */ /* returns 1 if not there */ -static int delete_extract_item(ditem) -DIR_ITEM *ditem; +static int +delete_extract_item( + DIR_ITEM *ditem) { EXTRACT_LIST *this; EXTRACT_LIST_ITEM *that, *prev; @@ -404,127 +705,201 @@ DIR_ITEM *ditem; return 1; } +static char * +merge_path( + char *path1, + char *path2) +{ + char *result; + int len = strlen(path1); + if (path1[len-1] == '/' && path2[0] == '/') { + result = stralloc2(path1, path2+1); + } else if (path1[len-1] != '/' && path2[0] != '/') { + result = vstralloc(path1, "/", path2, NULL); + } else { + result = stralloc2(path1, path2); + } + return result; +} -void add_glob(glob) -char *glob; +void +add_glob( + char * glob) { char *regex; char *regex_path; char *s; + char *uqglob; + char *dir; + char *sdir = NULL; + int result = 1; - regex = glob_to_regex(glob); - dbprintf(("add_glob (%s) -> %s\n", glob, regex)); - if ((s = validate_regexp(regex)) != NULL) { - printf("\"%s\" is not a valid shell wildcard pattern: ", glob); - puts(s); + if (disk_path == NULL) { + g_printf(_("Must select directory before adding files\n")); return; } - /* - * glob_to_regex() anchors the beginning of the pattern with ^, - * but we will be tacking it onto the end of the current directory - * in add_file, so strip that off. Also, it anchors the end with - * $, but we need to match an optional trailing /, so tack that on - * the end. - */ - regex_path = stralloc(regex + 1); - amfree(regex); - regex_path[strlen(regex_path) - 1] = '\0'; - strappend(regex_path, "[/]*$"); - add_file(glob, regex_path); - amfree(regex_path); + + uqglob = unquote_string(glob); + glob = file_of_path(uqglob, &dir); + if (dir) { + sdir = merge_path(mount_point, disk_path); + result = cd_glob(dir, 0); + amfree(dir); + } + if (result) { + regex = glob_to_regex(glob); + dbprintf(_("add_glob (%s) -> %s\n"), uqglob, regex); + if ((s = validate_regexp(regex)) != NULL) { + g_printf(_("%s is not a valid shell wildcard pattern: "), glob); + puts(s); + } else { + /* + * glob_to_regex() anchors the beginning of the pattern with ^, + * but we will be tacking it onto the end of the current directory + * in add_file, so strip that off. Also, it anchors the end with + * $, but we need to match an optional trailing /, so tack that on + * the end. + */ + regex_path = stralloc(regex + 1); + regex_path[strlen(regex_path) - 1] = '\0'; + strappend(regex_path, "[/]*$"); + add_file(uqglob, regex_path); + amfree(regex_path); + } + if (sdir) { + set_directory(sdir, 0); + } + amfree(regex); + } + amfree(sdir); + amfree(uqglob); + amfree(glob); } -void add_regex(regex) -char *regex; +void +add_regex( + char * regex) { char *s; + char *dir; + char *sdir = NULL; + char *uqregex; + char *newregex; + int result = 1; - if ((s = validate_regexp(regex)) != NULL) { - printf("\"%s\" is not a valid regular expression: ", regex); - puts(s); + if (disk_path == NULL) { + g_printf(_("Must select directory before adding files\n")); return; } - add_file(regex, regex); + + uqregex = unquote_string(regex); + newregex = file_of_path(uqregex, &dir); + if (dir) { + sdir = merge_path(mount_point, disk_path); + result = cd_regex(dir, 0); + amfree(dir); + } + + if (result) { + if ((s = validate_regexp(newregex)) != NULL) { + g_printf(_("\"%s\" is not a valid regular expression: "), newregex); + puts(s); + } else { + add_file(uqregex, newregex); + } + if (sdir) { + set_directory(sdir, 0); + } + } + amfree(sdir); + amfree(uqregex); + amfree(newregex); } -void add_file(path, regex) -char *path; -char *regex; +void +add_file( + char * path, + char * regex) { DIR_ITEM *ditem, lditem; char *path_on_disk = NULL; - char *path_on_disk_slash = NULL; char *cmd = NULL; char *err = NULL; int i; - int j; + ssize_t j; char *dir, *dir_undo, dir_undo_ch = '\0'; char *ditem_path = NULL; + char *qditem_path = NULL; char *l = NULL; int added; - char *s, *fp; + char *s, *fp, *quoted; int ch; int found_one; + int dir_entries; if (disk_path == NULL) { - printf("Must select directory before adding files\n"); + g_printf(_("Must select directory before adding files\n")); return; } memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */ - dbprintf(("add_file: Looking for \"%s\"\n", regex)); + dbprintf(_("add_file: Looking for \"%s\"\n"), regex); - /* remove "/" at end of path */ - j = strlen(regex)-1; - while(j >= 0 && regex[j] == '/') regex[j--] = '\0'; + if(strcmp(regex, "/[/]*$") == 0) { /* "/" behave like "." */ + regex = "\\.[/]*$"; + } + else if(strcmp(regex, "[^/]*[/]*$") == 0) { /* "*" */ + regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$"; + } else { + /* remove "/" at end of path */ + j = (ssize_t)(strlen(regex) - 1); + while(j >= 0 && regex[j] == '/') + regex[j--] = '\0'; + } /* convert path (assumed in cwd) to one on disk */ if (strcmp(disk_path, "/") == 0) { if (*regex == '/') { - if (strcmp(regex, "/[/]*$") == 0) { - /* We want '/' to match everything in directory... */ - path_on_disk = stralloc("/[^/]*[/]*$"); - } else { - /* No mods needed if already starts with '/' */ - path_on_disk = stralloc(regex); - } + /* No mods needed if already starts with '/' */ + path_on_disk = stralloc(regex); } else { /* Prepend '/' */ path_on_disk = stralloc2("/", regex); } } else { - char *clean_disk_path = clean_regex(disk_path); + char *clean_disk_path = clean_regex(disk_path, 0); path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL); amfree(clean_disk_path); } - path_on_disk_slash = stralloc2(path_on_disk, "/"); - - dbprintf(("add_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n", - regex, path_on_disk)); + dbprintf(_("add_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n"), + regex, path_on_disk); found_one = 0; + dir_entries = 0; for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem)) { - dbprintf(("add_file: Pondering ditem->path=\"%s\"\n", ditem->path)); - if (match(path_on_disk, ditem->path) - || match(path_on_disk_slash, ditem->path)) + dir_entries++; + quoted = quote_string(ditem->path); + dbprintf(_("add_file: Pondering ditem->path=%s\n"), quoted); + amfree(quoted); + if (match(path_on_disk, ditem->path)) { found_one = 1; - j = strlen(ditem->path); + j = (ssize_t)strlen(ditem->path); if((j > 0 && ditem->path[j-1] == '/') || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.')) { /* It is a directory */ - ditem_path = newstralloc(ditem_path, ditem->path); clean_pathname(ditem_path); - cmd = stralloc2("ORLD ", ditem_path); + qditem_path = quote_string(ditem_path); + cmd = newstralloc2(cmd, "ORLD ", qditem_path); + amfree(qditem_path); if(send_command(cmd) == -1) { amfree(cmd); amfree(ditem_path); amfree(path_on_disk); - amfree(path_on_disk_slash); exit(1); } amfree(cmd); @@ -533,28 +908,24 @@ char *regex; if ((i = get_reply_line()) == -1) { amfree(ditem_path); amfree(path_on_disk); - amfree(path_on_disk_slash); exit(1); } - if(i==0) /* assume something wrong */ - { + if(i==0) { /* assume something wrong */ amfree(ditem_path); amfree(path_on_disk); - amfree(path_on_disk_slash); l = reply_line(); - printf("%s\n", l); + g_printf("%s\n", l); return; } dir_undo = NULL; added=0; lditem.path = newstralloc(lditem.path, ditem->path); /* skip the last line -- duplicate of the preamble */ - while ((i = get_reply_line()) != 0) - { + + while ((i = get_reply_line()) != 0) { if (i == -1) { amfree(ditem_path); amfree(path_on_disk); - amfree(path_on_disk_slash); exit(1); } if(err) { @@ -570,76 +941,83 @@ char *regex; puts(l); continue; } -#define sc "201-" - if(strncmp(l, sc, sizeof(sc)-1) != 0) { - err = "bad reply: not 201-"; + + s = l; + if(strncmp_const_skip(l, "201-", s, ch) != 0) { + err = _("bad reply: not 201-"); continue; } - - s = l + sizeof(sc)-1; ch = *s++; -#undef sc + skip_whitespace(s, ch); if(ch == '\0') { - err = "bad reply: missing date field"; + err = _("bad reply: missing date field"); continue; } fp = s-1; skip_non_whitespace(s, ch); s[-1] = '\0'; lditem.date = newstralloc(lditem.date, fp); - s[-1] = ch; + s[-1] = (char)ch; skip_whitespace(s, ch); if(ch == '\0' || sscanf(s - 1, "%d", &lditem.level) != 1) { - err = "bad reply: cannot parse level field"; + err = _("bad reply: cannot parse level field"); continue; } skip_integer(s, ch); skip_whitespace(s, ch); if(ch == '\0') { - err = "bad reply: missing tape field"; + err = _("bad reply: missing tape field"); continue; } fp = s-1; - skip_non_whitespace(s, ch); + skip_quoted_string(s, ch); s[-1] = '\0'; - lditem.tape = newstralloc(lditem.tape, fp); - s[-1] = ch; + amfree(lditem.tape); + lditem.tape = unquote_string(fp); + s[-1] = (char)ch; if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) { + long long fileno_ = (long long)0; skip_whitespace(s, ch); - if(ch == '\0' || sscanf(s - 1, "%d", &lditem.fileno) != 1) { - err = "bad reply: cannot parse fileno field"; + if(ch == '\0' || + sscanf(s - 1, "%lld", &fileno_) != 1) { + err = _("bad reply: cannot parse fileno field"); continue; } + lditem.fileno = (off_t)fileno_; skip_integer(s, ch); } skip_whitespace(s, ch); if(ch == '\0') { - err = "bad reply: missing directory field"; + err = _("bad reply: missing directory field"); continue; } dir = s - 1; - skip_non_whitespace(s, ch); + skip_quoted_string(s, ch); dir_undo = s - 1; dir_undo_ch = *dir_undo; *dir_undo = '\0'; switch(add_extract_item(&lditem)) { case -1: - printf("System error\n"); - dbprintf(("add_file: (Failed) System error\n")); + g_printf(_("System error\n")); + dbprintf(_("add_file: (Failed) System error\n")); break; + case 0: - printf("Added dir %s at date %s\n", - ditem_path, lditem.date); - dbprintf(("add_file: (Successful) Added dir %s at date %s\n", - ditem_path,lditem.date)); + quoted = quote_string(lditem.path); + g_printf(_("Added dir %s at date %s\n"), + quoted, lditem.date); + dbprintf(_("add_file: (Successful) Added dir %s at date %s\n"), + quoted, lditem.date); + amfree(quoted); added=1; break; + case 1: break; } @@ -647,157 +1025,243 @@ char *regex; if(!server_happy()) { puts(reply_line()); } else if(err) { - puts(err); - puts(cmd); + if (*err) + puts(err); + if (cmd) + puts(cmd); } else if(added == 0) { - printf("dir %s already added\n", ditem_path); - dbprintf(("add_file: dir %s already added\n", ditem_path)); + quoted = quote_string(ditem_path); + g_printf(_("dir %s already added\n"), quoted); + dbprintf(_("add_file: dir %s already added\n"), quoted); + amfree(quoted); } } else /* It is a file */ { switch(add_extract_item(ditem)) { case -1: - printf("System error\n"); - dbprintf(("add_file: (Failed) System error\n")); + g_printf(_("System error\n")); + dbprintf(_("add_file: (Failed) System error\n")); break; + case 0: - printf("Added %s\n", ditem->path); - dbprintf(("add_file: (Successful) Added %s\n", - ditem->path)); + quoted = quote_string(ditem->path); + g_printf(_("Added file %s\n"), quoted); + dbprintf(_("add_file: (Successful) Added %s\n"), quoted); + amfree(quoted); break; + case 1: - printf("File %s already added\n", ditem->path); - dbprintf(("add_file: file %s already added\n", - ditem->path)); - break; + quoted = quote_string(ditem->path); + g_printf(_("File %s already added\n"), quoted); + dbprintf(_("add_file: file %s already added\n"), quoted); + amfree(quoted); } } } } - if (cmd != NULL) - amfree(cmd); + + amfree(cmd); amfree(ditem_path); amfree(path_on_disk); - amfree(path_on_disk_slash); + + amfree(lditem.path); + amfree(lditem.date); + amfree(lditem.tape); if(! found_one) { - printf("File %s doesn't exist in directory\n", path); - dbprintf(("add_file: (Failed) File %s doesn't exist in directory\n", - path)); + quoted = quote_string(path); + g_printf(_("File %s doesn't exist in directory\n"), quoted); + dbprintf(_("add_file: (Failed) File %s doesn't exist in directory\n"), + quoted); + amfree(quoted); } } -void delete_glob(glob) -char *glob; +void +delete_glob( + char * glob) { char *regex; char *regex_path; char *s; + char *uqglob; + char *newglob; + char *dir; + char *sdir = NULL; + int result = 1; - regex = glob_to_regex(glob); - dbprintf(("delete_glob (%s) -> %s\n", glob, regex)); - if ((s = validate_regexp(regex)) != NULL) { - printf("\"%s\" is not a valid shell wildcard pattern: ", glob); - puts(s); + if (disk_path == NULL) { + g_printf(_("Must select directory before adding files\n")); return; } - /* - * glob_to_regex() anchors the beginning of the pattern with ^, - * but we will be tacking it onto the end of the current directory - * in add_file, so strip that off. Also, it anchors the end with - * $, but we need to match an optional trailing /, so tack that on - * the end. - */ - regex_path = stralloc(regex + 1); - amfree(regex); - regex_path[strlen(regex_path) - 1] = '\0'; - strappend(regex_path, "[/]*$"); - delete_file(glob, regex_path); - amfree(regex_path); + + uqglob = unquote_string(glob); + newglob = file_of_path(uqglob, &dir); + if (dir) { + sdir = merge_path(mount_point, disk_path); + result = cd_glob(dir, 0); + amfree(dir); + } + if (result) { + regex = glob_to_regex(newglob); + dbprintf(_("delete_glob (%s) -> %s\n"), newglob, regex); + if ((s = validate_regexp(regex)) != NULL) { + g_printf(_("\"%s\" is not a valid shell wildcard pattern: "), + newglob); + puts(s); +} else { + /* + * glob_to_regex() anchors the beginning of the pattern with ^, + * but we will be tacking it onto the end of the current directory + * in add_file, so strip that off. Also, it anchors the end with + * $, but we need to match an optional trailing /, so tack that on + * the end. + */ + regex_path = stralloc(regex + 1); + regex_path[strlen(regex_path) - 1] = '\0'; + strappend(regex_path, "[/]*$"); + delete_file(uqglob, regex_path); + amfree(regex_path); + } + if (sdir) { + set_directory(sdir, 0); + } + amfree(regex); + } + amfree(sdir); + amfree(uqglob); + amfree(newglob); } -void delete_regex(regex) -char *regex; +void +delete_regex( + char * regex) { char *s; + char *dir; + char *sdir = NULL; + char *uqregex; + char *newregex; + int result = 1; - if ((s = validate_regexp(regex)) != NULL) { - printf("\"%s\" is not a valid regular expression: ", regex); - puts(s); + if (disk_path == NULL) { + g_printf(_("Must select directory before adding files\n")); return; } - delete_file(regex, regex); + + uqregex = unquote_string(regex); + newregex = file_of_path(uqregex, &dir); + if (dir) { + sdir = merge_path(mount_point, disk_path); + result = cd_regex(dir, 0); + amfree(dir); + } + + if (result == 1) { + if ((s = validate_regexp(newregex)) != NULL) { + g_printf(_("\"%s\" is not a valid regular expression: "), newregex); + puts(s); + } else { + delete_file(newregex, regex); + } + if (sdir) { + set_directory(sdir, 0); + } + } + amfree(sdir); + amfree(uqregex); + amfree(newregex); } -void delete_file(path, regex) -char *path; -char *regex; +void +delete_file( + char * path, + char * regex) { DIR_ITEM *ditem, lditem; char *path_on_disk = NULL; - char *path_on_disk_slash = NULL; char *cmd = NULL; char *err = NULL; int i; - int j; - char *date, *date_undo, date_undo_ch = '\0'; + ssize_t j; + char *date; char *tape, *tape_undo, tape_undo_ch = '\0'; - char *dir, *dir_undo, dir_undo_ch = '\0'; - int level, fileno; + char *dir_undo, dir_undo_ch = '\0'; + int level = 0; + off_t fileno; char *ditem_path = NULL; + char *qditem_path; char *l = NULL; int deleted; char *s; int ch; int found_one; + char *quoted; if (disk_path == NULL) { - printf("Must select directory before deleting files\n"); + g_printf(_("Must select directory before deleting files\n")); return; } memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */ - dbprintf(("delete_file: Looking for \"%s\"\n", path)); - /* remove "/" at the end of the path */ - j = strlen(regex)-1; - while(j >= 0 && regex[j] == '/') regex[j--] = '\0'; + dbprintf(_("delete_file: Looking for \"%s\"\n"), path); + + if (strcmp(regex, "[^/]*[/]*$") == 0) { + /* Looking for * find everything but single . */ + regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$"; + } else { + /* remove "/" at end of path */ + j = (ssize_t)(strlen(regex) - 1); + while(j >= 0 && regex[j] == '/') regex[j--] = '\0'; + } /* convert path (assumed in cwd) to one on disk */ - if (strcmp(disk_path, "/") == 0) - path_on_disk = stralloc2("/", regex); - else { - char *clean_disk_path = clean_regex(disk_path); + if (strcmp(disk_path, "/") == 0) { + if (*regex == '/') { + if (strcmp(regex, "/[/]*$") == 0) { + /* We want "/" to match the directory itself: "/." */ + path_on_disk = stralloc("/\\.[/]*$"); + } else { + /* No mods needed if already starts with '/' */ + path_on_disk = stralloc(regex); + } + } else { + /* Prepend '/' */ + path_on_disk = stralloc2("/", regex); + } + } else { + char *clean_disk_path = clean_regex(disk_path, 0); path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL); amfree(clean_disk_path); } - path_on_disk_slash = stralloc2(path_on_disk, "/"); - - dbprintf(("delete_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n", - regex, path_on_disk)); + dbprintf(_("delete_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n"), + regex, path_on_disk); found_one = 0; for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem)) { - dbprintf(("delete_file: Pondering ditem->path=\"%s\"\n", ditem->path)); - if (match(path_on_disk, ditem->path) - || match(path_on_disk_slash, ditem->path)) + quoted = quote_string(ditem->path); + dbprintf(_("delete_file: Pondering ditem->path=%s\n"), quoted); + amfree(quoted); + if (match(path_on_disk, ditem->path)) { found_one = 1; - j = strlen(ditem->path); + j = (ssize_t)strlen(ditem->path); if((j > 0 && ditem->path[j-1] == '/') || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.')) { /* It is a directory */ ditem_path = newstralloc(ditem_path, ditem->path); clean_pathname(ditem_path); - cmd = stralloc2("ORLD ", ditem_path); + qditem_path = quote_string(ditem_path); + cmd = newstralloc2(cmd, "ORLD ", qditem_path); + amfree(qditem_path); if(send_command(cmd) == -1) { amfree(cmd); amfree(ditem_path); amfree(path_on_disk); - amfree(path_on_disk_slash); exit(1); } amfree(cmd); @@ -805,36 +1269,33 @@ char *regex; if ((i = get_reply_line()) == -1) { amfree(ditem_path); amfree(path_on_disk); - amfree(path_on_disk_slash); exit(1); } if(i==0) /* assume something wrong */ { amfree(ditem_path); amfree(path_on_disk); - amfree(path_on_disk_slash); l = reply_line(); - printf("%s\n", l); + g_printf("%s\n", l); return; } deleted=0; lditem.path = newstralloc(lditem.path, ditem->path); amfree(cmd); - date_undo = tape_undo = dir_undo = NULL; + tape_undo = dir_undo = NULL; /* skip the last line -- duplicate of the preamble */ while ((i = get_reply_line()) != 0) { if (i == -1) { amfree(ditem_path); amfree(path_on_disk); - amfree(path_on_disk_slash); exit(1); } if(err) { if(cmd == NULL) { if(tape_undo) *tape_undo = tape_undo_ch; if(dir_undo) *dir_undo = dir_undo_ch; - date_undo = tape_undo = dir_undo = NULL; + tape_undo = dir_undo = NULL; cmd = stralloc(l); /* save for the error report */ } continue; /* throw the rest of the lines away */ @@ -844,35 +1305,33 @@ char *regex; puts(l); continue; } -#define sc "201-" - if(strncmp(l, sc, sizeof(sc)-1) != 0) { - err = "bad reply: not 201-"; + + s = l; + if(strncmp_const_skip(l, "201-", s, ch) != 0) { + err = _("bad reply: not 201-"); continue; } - s = l + sizeof(sc)-1; ch = *s++; -#undef sc + skip_whitespace(s, ch); if(ch == '\0') { - err = "bad reply: missing date field"; + err = _("bad reply: missing date field"); continue; } date = s - 1; skip_non_whitespace(s, ch); - date_undo = s - 1; - date_undo_ch = *date_undo; - *date_undo = '\0'; + *(s - 1) = '\0'; skip_whitespace(s, ch); if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) { - err = "bad reply: cannot parse level field"; + err = _("bad reply: cannot parse level field"); continue; } skip_integer(s, ch); skip_whitespace(s, ch); if(ch == '\0') { - err = "bad reply: missing tape field"; + err = _("bad reply: missing tape field"); continue; } tape = s - 1; @@ -882,20 +1341,22 @@ char *regex; *tape_undo = '\0'; if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) { + long long fileno_ = (long long)0; skip_whitespace(s, ch); - if(ch == '\0' || sscanf(s - 1, "%d", &fileno) != 1) { - err = "bad reply: cannot parse fileno field"; + if(ch == '\0' || + sscanf(s - 1, "%lld", &fileno_) != 1) { + err = _("bad reply: cannot parse fileno field"); continue; } + fileno = (off_t)fileno_; skip_integer(s, ch); } skip_whitespace(s, ch); if(ch == '\0') { - err = "bad reply: missing directory field"; + err = _("bad reply: missing directory field"); continue; } - dir = s - 1; skip_non_whitespace(s, ch); dir_undo = s - 1; dir_undo_ch = *dir_undo; @@ -906,13 +1367,13 @@ char *regex; lditem.tape = newstralloc(lditem.tape, tape); switch(delete_extract_item(&lditem)) { case -1: - printf("System error\n"); - dbprintf(("delete_file: (Failed) System error\n")); + g_printf(_("System error\n")); + dbprintf(_("delete_file: (Failed) System error\n")); break; case 0: - printf("Deleted dir %s at date %s\n", ditem_path, date); - dbprintf(("delete_file: (Successful) Deleted dir %s at date %s\n", - ditem_path, date)); + g_printf(_("Deleted dir %s at date %s\n"), ditem_path, date); + dbprintf(_("delete_file: (Successful) Deleted dir %s at date %s\n"), + ditem_path, date); deleted=1; break; case 1: @@ -922,34 +1383,34 @@ char *regex; if(!server_happy()) { puts(reply_line()); } else if(err) { - if(*err) { + if (*err) puts(err); - } - puts(cmd); + if (cmd) + puts(cmd); } else if(deleted == 0) { - printf("Warning - dir '%s' not on tape list\n", + g_printf(_("Warning - dir '%s' not on tape list\n"), ditem_path); - dbprintf(("delete_file: dir '%s' not on tape list\n", - ditem_path)); + dbprintf(_("delete_file: dir '%s' not on tape list\n"), + ditem_path); } } else { switch(delete_extract_item(ditem)) { case -1: - printf("System error\n"); - dbprintf(("delete_file: (Failed) System error\n")); + g_printf(_("System error\n")); + dbprintf(_("delete_file: (Failed) System error\n")); break; case 0: - printf("Deleted %s\n", ditem->path); - dbprintf(("delete_file: (Successful) Deleted %s\n", - ditem->path)); + g_printf(_("Deleted %s\n"), ditem->path); + dbprintf(_("delete_file: (Successful) Deleted %s\n"), + ditem->path); break; case 1: - printf("Warning - file '%s' not on tape list\n", + g_printf(_("Warning - file '%s' not on tape list\n"), ditem->path); - dbprintf(("delete_file: file '%s' not on tape list\n", - ditem->path)); + dbprintf(_("delete_file: file '%s' not on tape list\n"), + ditem->path); break; } } @@ -958,25 +1419,26 @@ char *regex; amfree(cmd); amfree(ditem_path); amfree(path_on_disk); - amfree(path_on_disk_slash); if(! found_one) { - printf("File %s doesn't exist in directory\n", path); - dbprintf(("delete_file: (Failed) File %s doesn't exist in directory\n", - path)); + g_printf(_("File %s doesn't exist in directory\n"), path); + dbprintf(_("delete_file: (Failed) File %s doesn't exist in directory\n"), + path); } } /* print extract list into file. If NULL ptr passed print to screen */ -void display_extract_list(file) -char *file; +void +display_extract_list( + char * file) { EXTRACT_LIST *this; EXTRACT_LIST_ITEM *that; FILE *fp; char *pager; char *pager_command; + char *uqfile; if (file == NULL) { @@ -991,39 +1453,64 @@ char *file; pager_command = stralloc2(pager, " ; /bin/cat > /dev/null"); if ((fp = popen(pager_command, "w")) == NULL) { - printf("Warning - can't pipe through %s\n", pager); + g_printf(_("Warning - can't pipe through %s\n"), pager); fp = stdout; } amfree(pager_command); } else { - if ((fp = fopen(file, "w")) == NULL) + uqfile = unquote_string(file); + if ((fp = fopen(uqfile, "w")) == NULL) { - printf("Can't open file '%s' to print extract list into\n", file); + g_printf(_("Can't open file %s to print extract list into\n"), file); + amfree(uqfile); return; } + amfree(uqfile); } for (this = extract_list; this != NULL; this = this->next) { - fprintf(fp, "TAPE %s LEVEL %d DATE %s\n", + g_fprintf(fp, _("TAPE %s LEVEL %d DATE %s\n"), this->tape, this->level, this->date); for (that = this->files; that != NULL; that = that->next) - fprintf(fp, "\t%s\n", that->path); + g_fprintf(fp, "\t%s\n", that->path); } if (file == NULL) { apclose(fp); } else { - printf("Extract list written to file %s\n", file); + g_printf(_("Extract list written to file %s\n"), file); afclose(fp); } } +static int +is_empty_dir( + char *fname) +{ + DIR *dir; + struct dirent *entry; + int gotentry; + + if((dir = opendir(fname)) == NULL) + return 1; + + gotentry = 0; + while(!gotentry && (entry = readdir(dir)) != NULL) { + gotentry = !is_dot_or_dotdot(entry->d_name); + } + + closedir(dir); + return !gotentry; + +} + /* returns 0 if extract list empty and 1 if it isn't */ -int is_extract_list_nonempty P((void)) +int +is_extract_list_nonempty(void) { return (extract_list != NULL); } @@ -1031,30 +1518,31 @@ int is_extract_list_nonempty P((void)) /* prints continue prompt and waits for response, returns 0 if don't, non-0 if do */ -static int okay_to_continue(allow_tape, allow_skip, allow_retry) - int allow_tape; - int allow_skip; - int allow_retry; +static int +okay_to_continue( + int allow_tape, + int allow_skip, + int allow_retry) { int ch; int ret = -1; char *line = NULL; char *s; char *prompt; - int get_tape; + int get_device; - get_tape = 0; + get_device = 0; while (ret < 0) { - if (get_tape) { - prompt = "New tape device [?]: "; + if (get_device) { + prompt = _("New device name [?]: "); } else if (allow_tape && allow_skip) { - prompt = "Continue [?/Y/n/s/t]? "; + prompt = _("Continue [?/Y/n/s/d]? "); } else if (allow_tape && !allow_skip) { - prompt = "Continue [?/Y/n/t]? "; + prompt = _("Continue [?/Y/n/d]? "); } else if (allow_retry) { - prompt = "Continue [?/Y/n/r]? "; + prompt = _("Continue [?/Y/n/r]? "); } else { - prompt = "Continue [?/Y/n]? "; + prompt = _("Continue [?/Y/n]? "); } fputs(prompt, stdout); fflush(stdout); fflush(stderr); @@ -1062,38 +1550,53 @@ static int okay_to_continue(allow_tape, allow_skip, allow_retry) if ((line = agets(stdin)) == NULL) { putchar('\n'); clearerr(stdin); - if (get_tape) { - get_tape = 0; + if (get_device) { + get_device = 0; continue; } ret = 0; break; } + dbprintf("User prompt: '%s'; response: '%s'\n", prompt, line); + s = line; - while ((ch = *s++) != '\0' && isspace(ch)) {} + while ((ch = *s++) != '\0' && g_ascii_isspace(ch)) { + (void)ch; /* Quiet empty loop compiler warning */ + } if (ch == '?') { - if (get_tape) { - printf("Enter a new device ([host:]device) or \"default\"\n"); + if (get_device) { + g_printf(_("Enter a new device name or \"default\"\n")); } else { - printf("Enter \"y\"es to continue, \"n\"o to stop"); + g_printf(_("Enter \"y\"es to continue, \"n\"o to stop")); if(allow_skip) { - printf(", \"s\"kip this tape"); + g_printf(_(", \"s\"kip this tape")); } if(allow_retry) { - printf(" or \"r\"etry this tape"); + g_printf(_(" or \"r\"etry this tape")); } if (allow_tape) { - printf(" or \"t\"ape to change tape drives"); + g_printf(_(" or \"d\" to change to a new device")); } putchar('\n'); } - } else if (get_tape) { - set_tape(s - 1); - get_tape = 0; + } else if (get_device) { + char *tmp = stralloc(tape_server_name); + + if (strncmp_const(s - 1, "default") == 0) { + set_device(tmp, NULL); /* default device, existing host */ + } else if (s[-1] != '\0') { + set_device(tmp, s - 1); /* specified device, existing host */ + } else { + g_printf(_("No change.\n")); + } + + amfree(tmp); + + get_device = 0; } else if (ch == '\0' || ch == 'Y' || ch == 'y') { ret = 1; - } else if (allow_tape && (ch == 'T' || ch == 't')) { - get_tape = 1; + } else if (allow_tape && (ch == 'D' || ch == 'd' || ch == 'T' || ch == 't')) { + get_device = 1; /* ('T' and 't' are for backward-compatibility) */ } else if (ch == 'N' || ch == 'n') { ret = 0; } else if (allow_retry && (ch == 'R' || ch == 'r')) { @@ -1102,20 +1605,25 @@ static int okay_to_continue(allow_tape, allow_skip, allow_retry) ret = SKIP_TAPE; } } + /*@ignore@*/ amfree(line); + /*@end@*/ return ret; } -static void send_to_tape_server(tss, cmd) -int tss; -char *cmd; +static void +send_to_tape_server( + security_stream_t * stream, + char * cmd) { char *msg = stralloc2(cmd, "\r\n"); - if (fullwrite(tss, msg, strlen(msg)) < 0) + g_debug("send_to_tape_server: %s\n", cmd); + if (security_stream_write(stream, msg, strlen(msg)) < 0) { - error("Error writing to tape server"); + error(_("Error writing to tape server")); exit(101); + /*NOTREACHED*/ } amfree(msg); } @@ -1124,105 +1632,42 @@ char *cmd; /* start up connection to tape server and set commands to initiate transfer of dump image. Return tape server socket on success, -1 on error. */ -static int extract_files_setup(label, fsf) -char *label; -int fsf; +static int +extract_files_setup( + char * label, + off_t fsf) { - struct servent *sp; - int my_port, my_data_port; char *disk_regex = NULL; char *host_regex = NULL; - char *service_name = NULL; - char *line = NULL; char *clean_datestamp, *ch, *ch1; - char *our_feature_string = NULL; char *tt = NULL; + char *req; + int response_error; - service_name = stralloc2("amidxtape", SERVICE_SUFFIX); - - /* get tape server details */ - if ((sp = getservbyname(service_name, "tcp")) == NULL) - { - printf("%s/tcp unknown protocol - config error?\n", service_name); - amfree(service_name); - return -1; - } - amfree(service_name); - seteuid(0); /* it either works ... */ - setegid(0); - tape_control_sock = stream_client_privileged(tape_server_name, - ntohs(sp->s_port), - -1, - STREAM_BUFSIZE, - &my_port, - 0); - if (tape_control_sock < 0) - { - printf("cannot connect to %s: %s\n", tape_server_name, strerror(errno)); - return -1; - } - if (my_port >= IPPORT_RESERVED) { - aclose(tape_control_sock); - printf("did not get a reserved port: %d\n", my_port); - return -1; - } - - setegid(getgid()); - seteuid(getuid()); /* put it back */ - - /* do the security thing */ - line = get_security(); - send_to_tape_server(tape_control_sock, line); - memset(line, '\0', strlen(line)); - amfree(line); - - disk_regex = alloc(strlen(disk_name) * 2 + 3); - - ch = disk_name; - ch1 = disk_regex; - - /* we want to force amrestore to only match disk_name exactly */ - *(ch1++) = '^'; - - /* We need to escape some characters first... NT compatibilty crap */ - for (; *ch != 0; ch++, ch1++) { - switch (*ch) { /* done this way in case there are more */ - case '$': - *(ch1++) = '\\'; - /* no break; we do want to fall through... */ - default: - *ch1 = *ch; - } + amidxtaped_secdrv = security_getdriver(authopt); + if (amidxtaped_secdrv == NULL) { + error(_("no '%s' security driver available for host '%s'"), + authopt, tape_server_name); } - /* we want to force amrestore to only match disk_name exactly */ - *(ch1++) = '$'; - - *ch1 = '\0'; - - host_regex = alloc(strlen(dump_hostname) * 2 + 3); - - ch = dump_hostname; - ch1 = host_regex; - - /* we want to force amrestore to only match dump_hostname exactly */ - *(ch1++) = '^'; - - /* We need to escape some characters first... NT compatibilty crap */ - for (; *ch != 0; ch++, ch1++) { - switch (*ch) { /* done this way in case there are more */ - case '$': - *(ch1++) = '\\'; - /* no break; we do want to fall through... */ - default: - *ch1 = *ch; - } + /* We assume that amidxtaped support fe_amidxtaped_options_features */ + /* and fe_amidxtaped_options_auth */ + /* We should send a noop to really know */ + req = vstralloc("SERVICE amidxtaped\n", + "OPTIONS ", "features=", our_features_string, ";", + "auth=", authopt, ";", + "\n", NULL); + protocol_sendreq(tape_server_name, amidxtaped_secdrv, + generic_client_get_security_conf, req, STARTUP_TIMEOUT, + amidxtaped_response, &response_error); + amfree(req); + protocol_run(); + if(response_error != 0) { + return -1; } - /* we want to force amrestore to only match dump_hostname exactly */ - *(ch1++) = '$'; - - *ch1 = '\0'; + disk_regex = make_exact_disk_expression(disk_name); + host_regex = make_exact_host_expression(dump_hostname); clean_datestamp = stralloc(dump_datestamp); for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) { @@ -1232,22 +1677,25 @@ int fsf; } } *ch = '\0'; - /* push our feature list off to the tape server */ /* XXX assumes that index server and tape server are equivalent, ew */ - if(am_has_feature(indexsrv_features, fe_amidxtaped_exchange_features)){ - char buffer[32768] = "\0"; - our_feature_string = am_feature_to_string(our_features); - tt = newstralloc2(tt, "FEATURES=", our_feature_string); - send_to_tape_server(tape_control_sock, tt); - if (read(tape_control_sock, buffer, sizeof(buffer)) <= 0) { - error("Could not read features from control socket\n"); - /* NOTREACHED */ + if(am_has_feature(indexsrv_features, fe_amidxtaped_exchange_features)){ + tt = newstralloc2(tt, "FEATURES=", our_features_string); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt); + get_amidxtaped_line(); + if(strncmp_const(amidxtaped_line,"FEATURES=") == 0) { + tapesrv_features = am_string_to_feature(amidxtaped_line+9); + } else { + g_fprintf(stderr, _("amrecover - expecting FEATURES line from amidxtaped\n")); + stop_amidxtaped(); + amfree(disk_regex); + amfree(host_regex); + amfree(clean_datestamp); + return -1; } - - tapesrv_features = am_string_to_feature(buffer); - amfree(our_feature_string); + } else { + *tapesrv_features = *indexsrv_features; } @@ -1258,30 +1706,30 @@ int fsf; am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) { if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) { - tt = newstralloc2(tt, "CONFIG=", config); - send_to_tape_server(tape_control_sock, tt); + tt = newstralloc2(tt, "CONFIG=", get_config_name()); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt); } if(am_has_feature(indexsrv_features, fe_amidxtaped_label) && label && label[0] != '/') { tt = newstralloc2(tt,"LABEL=",label); - send_to_tape_server(tape_control_sock, tt); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt); } if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) { char v_fsf[100]; - snprintf(v_fsf, 99, "%d", fsf); + g_snprintf(v_fsf, 99, "%lld", (long long)fsf); tt = newstralloc2(tt, "FSF=",v_fsf); - send_to_tape_server(tape_control_sock, tt); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt); } - send_to_tape_server(tape_control_sock, "HEADER"); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, "HEADER"); tt = newstralloc2(tt, "DEVICE=", dump_device_name); - send_to_tape_server(tape_control_sock, tt); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt); tt = newstralloc2(tt, "HOST=", host_regex); - send_to_tape_server(tape_control_sock, tt); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt); tt = newstralloc2(tt, "DISK=", disk_regex); - send_to_tape_server(tape_control_sock, tt); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt); tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp); - send_to_tape_server(tape_control_sock, tt); - send_to_tape_server(tape_control_sock, "END"); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, "END"); amfree(tt); } else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) { @@ -1294,121 +1742,82 @@ int fsf; * "diskname" * "datestamp" */ - send_to_tape_server(tape_control_sock, "6"); - send_to_tape_server(tape_control_sock, "-h"); - send_to_tape_server(tape_control_sock, "-p"); - send_to_tape_server(tape_control_sock, dump_device_name); - send_to_tape_server(tape_control_sock, host_regex); - send_to_tape_server(tape_control_sock, disk_regex); - send_to_tape_server(tape_control_sock, clean_datestamp); - - dbprintf(("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n", - dump_device_name, host_regex, disk_regex, clean_datestamp)); - } - - /* - * split-restoring amidxtaped versions will expect to set up a data - * connection for dumpfile data, distinct from the socket we're already - * using for control data - */ - - if(am_has_feature(tapesrv_features, fe_recover_splits)){ - char buffer[32768]; - int data_port = -1; - int nread; - - nread = read(tape_control_sock, buffer, sizeof(buffer)); - - if (nread <= 0) { - error("Could not read from control socket: %s\n", - strerror(errno)); - /* NOTREACHED */ - } - - buffer[nread] = '\0'; - if (sscanf(buffer, "CONNECT %d\n", &data_port) != 1) { - error("Recieved invalid port number message from control socket: %s\n", - buffer); - /* NOTREACHED */ - } - - tape_data_sock = stream_client_privileged(server_name, - data_port, - -1, - STREAM_BUFSIZE, - &my_data_port, - 0); - if(tape_data_sock == -1){ - error("Unable to make data connection to server: %s\n", - strerror(errno)); - /* NOTREACHED */ - } - - amfree(our_feature_string); - - line = get_security(); - - send_to_tape_server(tape_data_sock, line); - memset(line, '\0', strlen(line)); - amfree(line); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, "6"); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, "-h"); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, "-p"); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, dump_device_name); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, host_regex); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, disk_regex); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, clean_datestamp); + + dbprintf(_("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n"), + dump_device_name, host_regex, disk_regex, clean_datestamp); } amfree(disk_regex); amfree(host_regex); amfree(clean_datestamp); - return tape_control_sock; + return 0; } -void read_file_header(buffer, file, buflen, tapedev) -char *buffer; -dumpfile_t *file; -size_t buflen; -int tapedev; /* * Reads the first block of a tape file. */ + +void +read_file_header( + char * buffer, + dumpfile_t *file, + size_t buflen, + int tapedev) { ssize_t bytes_read; - bytes_read = read_buffer(tapedev, buffer, buflen, READ_TIMEOUT); if(bytes_read < 0) { - error("error reading header (%s), check amidxtaped.*.debug on server", + error(_("error reading header (%s), check amidxtaped.*.debug on server"), strerror(errno)); - /* NOTREACHED */ + /*NOTREACHED*/ } if((size_t)bytes_read < buflen) { - fprintf(stderr, "%s: short block %d byte%s\n", - get_pname(), (int)bytes_read, (bytes_read == 1) ? "" : "s"); + g_fprintf(stderr, plural(_("%s: short block %d byte\n"), + _("%s: short block %d bytes\n"), bytes_read), + get_pname(), (int)bytes_read); print_header(stdout, file); - error("Can't read file header"); - /* NOTREACHED */ + error(_("Can't read file header")); + /*NOTREACHED*/ } /* bytes_read == buflen */ - parse_file_header(buffer, file, bytes_read); + parse_file_header(buffer, file, (size_t)bytes_read); } -enum dumptypes {IS_UNKNOWN, IS_DUMP, IS_GNUTAR, IS_TAR, IS_SAMBA, IS_SAMBA_TAR}; - -static void extract_files_child(in_fd, elist) - int in_fd; - EXTRACT_LIST *elist; +enum dumptypes { + IS_UNKNOWN, + IS_DUMP, + IS_GNUTAR, + IS_TAR, + IS_SAMBA, + IS_SAMBA_TAR, + IS_APPLICATION_API +}; + +static void +extract_files_child( + ctl_data_t *ctl_data) { int save_errno; - int extra_params = 0; - int i,j=0; - char **restore_args = NULL; + int i; + guint j; + GPtrArray *argv_ptr = g_ptr_array_new(); int files_off_tape; EXTRACT_LIST_ITEM *fn; enum dumptypes dumptype = IS_UNKNOWN; - char buffer[DISK_BLOCK_BYTES]; - dumpfile_t file; size_t len_program; char *cmd = NULL; - int passwd_field = -1; + guint passwd_field = 999999999; #ifdef SAMBA_CLIENT char *domain = NULL, *smbpass = NULL; #endif @@ -1417,37 +1826,35 @@ static void extract_files_child(in_fd, elist) /* never returns */ /* make in_fd be our stdin */ - if (dup2(in_fd, STDIN_FILENO) == -1) + if (dup2(ctl_data->child_pipe[0], STDIN_FILENO) == -1) { - error("dup2 failed in extract_files_child: %s", strerror(errno)); - /* NOTREACHED */ + error(_("dup2 failed in extract_files_child: %s"), strerror(errno)); + /*NOTREACHED*/ } - /* read the file header */ - fh_init(&file); - read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO); - - if(file.type != F_DUMPFILE) { - print_header(stdout, &file); - error("bad header"); - /* NOTREACHED */ + if(ctl_data->file.type != F_DUMPFILE) { + dump_dumpfile_t(&ctl_data->file); + error(_("bad header")); + /*NOTREACHED*/ } - if (file.program != NULL) { + if (ctl_data->file.program != NULL) { + if (strcmp(ctl_data->file.program, "APPLICATION") == 0) + dumptype = IS_APPLICATION_API; #ifdef GNUTAR - if (strcmp(file.program, GNUTAR) == 0) + if (strcmp(ctl_data->file.program, GNUTAR) == 0) dumptype = IS_GNUTAR; #endif if (dumptype == IS_UNKNOWN) { - len_program = strlen(file.program); + len_program = strlen(ctl_data->file.program); if(len_program >= 3 && - strcmp(&file.program[len_program-3],"tar") == 0) + strcmp(&ctl_data->file.program[len_program-3],"tar") == 0) dumptype = IS_TAR; } #ifdef SAMBA_CLIENT - if (dumptype == IS_UNKNOWN && strcmp(file.program, SAMBA_CLIENT) ==0) { + if (dumptype == IS_UNKNOWN && strcmp(ctl_data->file.program, SAMBA_CLIENT) ==0) { if (samba_extract_method == SAMBA_TAR) dumptype = IS_SAMBA_TAR; else @@ -1457,135 +1864,151 @@ static void extract_files_child(in_fd, elist) } /* form the arguments to restore */ - files_off_tape = length_of_tape_list(elist); - switch (dumptype) { - case IS_SAMBA: -#ifdef SAMBA_CLIENT - extra_params = 10; - break; -#endif - case IS_TAR: - case IS_GNUTAR: - extra_params = 4; - break; - case IS_SAMBA_TAR: - extra_params = 3; - break; - case IS_UNKNOWN: - case IS_DUMP: -#ifdef AIX_BACKUP - extra_params = 2; -#else -#if defined(XFSDUMP) - if (strcmp(file.program, XFSDUMP) == 0) { - extra_params = 4 + files_off_tape; - } else -#endif - { - extra_params = 4; - } -#endif - break; - } - - restore_args = (char **)alloc((extra_params + files_off_tape + 1) - * sizeof(char *)); + files_off_tape = length_of_tape_list(ctl_data->elist); switch(dumptype) { case IS_SAMBA: #ifdef SAMBA_CLIENT - restore_args[j++] = stralloc("smbclient"); - smbpass = findpass(file.disk, &domain); - if (smbpass) { - restore_args[j++] = stralloc(file.disk); - passwd_field=j; - restore_args[j++] = stralloc("-U"); - restore_args[j++] = smbpass; - if (domain) { - restore_args[j++] = stralloc("-W"); - restore_args[j++] = stralloc(domain); - } else - extra_params -= 2; - } else - extra_params -= 6; - restore_args[j++] = stralloc("-d0"); - restore_args[j++] = stralloc("-Tx"); - restore_args[j++] = stralloc("-"); /* data on stdin */ - break; + g_ptr_array_add(argv_ptr, stralloc("smbclient")); + smbpass = findpass(ctl_data->file.disk, &domain); + if (smbpass) { + g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.disk)); + g_ptr_array_add(argv_ptr, stralloc("-U")); + passwd_field = argv_ptr->len; + g_ptr_array_add(argv_ptr, stralloc(smbpass)); + if (domain) { + g_ptr_array_add(argv_ptr, stralloc("-W")); + g_ptr_array_add(argv_ptr, stralloc(domain)); + } + } + g_ptr_array_add(argv_ptr, stralloc("-d0")); + g_ptr_array_add(argv_ptr, stralloc("-Tx")); + g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */ + break; #endif case IS_TAR: case IS_GNUTAR: - restore_args[j++] = stralloc("tar"); - restore_args[j++] = stralloc("--numeric-owner"); - restore_args[j++] = stralloc("-xpGvf"); - restore_args[j++] = stralloc("-"); /* data on stdin */ + g_ptr_array_add(argv_ptr, stralloc("tar")); + /* ignore trailing zero blocks on input (this was the default until tar-1.21) */ + g_ptr_array_add(argv_ptr, stralloc("--ignore-zeros")); + g_ptr_array_add(argv_ptr, stralloc("--numeric-owner")); + g_ptr_array_add(argv_ptr, stralloc("-xpGvf")); + g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */ break; case IS_SAMBA_TAR: - restore_args[j++] = stralloc("tar"); - restore_args[j++] = stralloc("-xpvf"); - restore_args[j++] = stralloc("-"); /* data on stdin */ + g_ptr_array_add(argv_ptr, stralloc("tar")); + g_ptr_array_add(argv_ptr, stralloc("-xpvf")); + g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */ break; case IS_UNKNOWN: case IS_DUMP: - restore_args[j++] = stralloc("restore"); + g_ptr_array_add(argv_ptr, stralloc("restore")); #ifdef AIX_BACKUP restore_args[j++] = stralloc("-xB"); + g_ptr_array_add(argv_ptr, stralloc("-xB")); #else #if defined(XFSDUMP) - if (strcmp(file.program, XFSDUMP) == 0) { - restore_args[j++] = stralloc("-v"); - restore_args[j++] = stralloc("silent"); + if (strcmp(ctl_data->file.program, XFSDUMP) == 0) { + g_ptr_array_add(argv_ptr, stralloc("-v")); + g_ptr_array_add(argv_ptr, stralloc("silent")); } else #endif #if defined(VDUMP) - if (strcmp(file.program, VDUMP) == 0) { - restore_args[j++] = stralloc("xf"); - restore_args[j++] = stralloc("-"); /* data on stdin */ + if (strcmp(ctl_data->file.program, VDUMP) == 0) { + g_ptr_array_add(argv_ptr, stralloc("xf")); + g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */ } else #endif { - restore_args[j++] = stralloc("xbf"); - restore_args[j++] = stralloc("2"); /* read in units of 1K */ - restore_args[j++] = stralloc("-"); /* data on stdin */ + g_ptr_array_add(argv_ptr, stralloc("xbf")); + g_ptr_array_add(argv_ptr, stralloc("2")); /* read in units of 1K */ + g_ptr_array_add(argv_ptr, stralloc("-")); /* data on stdin */ } #endif + break; + case IS_APPLICATION_API: + g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.application)); + g_ptr_array_add(argv_ptr, stralloc("restore")); + g_ptr_array_add(argv_ptr, stralloc("--config")); + g_ptr_array_add(argv_ptr, stralloc(get_config_name())); + g_ptr_array_add(argv_ptr, stralloc("--disk")); + g_ptr_array_add(argv_ptr, stralloc(ctl_data->file.disk)); + if (dump_dle && dump_dle->device) { + g_ptr_array_add(argv_ptr, stralloc("--device")); + g_ptr_array_add(argv_ptr, stralloc(dump_dle->device)); + } + if (ctl_data->data_path == DATA_PATH_DIRECTTCP) { + g_ptr_array_add(argv_ptr, stralloc("--data-path")); + g_ptr_array_add(argv_ptr, stralloc("DIRECTTCP")); + g_ptr_array_add(argv_ptr, stralloc("--direct-tcp")); + g_ptr_array_add(argv_ptr, stralloc(ctl_data->addrs)); + } + if (ctl_data->bsu && ctl_data->bsu->smb_recover_mode && + samba_extract_method == SAMBA_SMBCLIENT){ + g_ptr_array_add(argv_ptr, stralloc("--recover-mode")); + g_ptr_array_add(argv_ptr, stralloc("smb")); + } + g_ptr_array_add(argv_ptr, stralloc("--level")); + g_ptr_array_add(argv_ptr, g_strdup_printf("%d", ctl_data->elist->level)); + if (dump_dle) { + GSList *scriptlist; + script_t *script; + + merge_properties(dump_dle->application_property, proplist); + application_property_add_to_argv(argv_ptr, dump_dle, NULL, + tapesrv_features); + for (scriptlist = dump_dle->scriptlist; scriptlist != NULL; + scriptlist = scriptlist->next) { + script = (script_t *)scriptlist->data; + if (script->result && script->result->proplist) { + property_add_to_argv(argv_ptr, script->result->proplist); + } + } + + } else if (proplist) { + property_add_to_argv(argv_ptr, proplist); + } + break; } - - for (i = 0, fn = elist->files; i < files_off_tape; i++, fn = fn->next) + + for (i = 0, fn = ctl_data->elist->files; i < files_off_tape; + i++, fn = fn->next) { switch (dumptype) { + case IS_APPLICATION_API: case IS_TAR: case IS_GNUTAR: case IS_SAMBA_TAR: case IS_SAMBA: if (strcmp(fn->path, "/") == 0) - restore_args[j++] = stralloc("."); + g_ptr_array_add(argv_ptr, stralloc(".")); else - restore_args[j++] = stralloc2(".", fn->path); + g_ptr_array_add(argv_ptr, stralloc2(".", fn->path)); break; case IS_UNKNOWN: case IS_DUMP: #if defined(XFSDUMP) - if (strcmp(file.program, XFSDUMP) == 0) { + if (strcmp(ctl_data->file.program, XFSDUMP) == 0) { /* * xfsrestore needs a -s option before each file to be * restored, and also wants them to be relative paths. */ - restore_args[j++] = stralloc("-s"); - restore_args[j++] = stralloc(fn->path + 1); + g_ptr_array_add(argv_ptr, stralloc("-s")); + g_ptr_array_add(argv_ptr, stralloc(fn->path + 1)); } else #endif { - restore_args[j++] = stralloc(fn->path); + g_ptr_array_add(argv_ptr, stralloc(fn->path)); } + break; } } #if defined(XFSDUMP) - if (strcmp(file.program, XFSDUMP) == 0) { - restore_args[j++] = stralloc("-"); - restore_args[j++] = stralloc("."); + if (strcmp(ctl_data->file.program, XFSDUMP) == 0) { + g_ptr_array_add(argv_ptr, stralloc("-")); + g_ptr_array_add(argv_ptr, stralloc(".")); } #endif - restore_args[j] = NULL; + g_ptr_array_add(argv_ptr, NULL); switch (dumptype) { case IS_SAMBA: @@ -1599,7 +2022,7 @@ static void extract_files_child(in_fd, elist) case IS_GNUTAR: case IS_SAMBA_TAR: #ifndef GNUTAR - fprintf(stderr, "warning: GNUTAR program not available.\n"); + g_fprintf(stderr, _("warning: GNUTAR program not available.\n")); cmd = stralloc("tar"); #else cmd = stralloc(GNUTAR); @@ -1609,49 +2032,51 @@ static void extract_files_child(in_fd, elist) case IS_DUMP: cmd = NULL; #if defined(DUMP) - if (strcmp(file.program, DUMP) == 0) { + if (strcmp(ctl_data->file.program, DUMP) == 0) { cmd = stralloc(RESTORE); } #endif #if defined(VDUMP) - if (strcmp(file.program, VDUMP) == 0) { + if (strcmp(ctl_data->file.program, VDUMP) == 0) { cmd = stralloc(VRESTORE); } #endif #if defined(VXDUMP) - if (strcmp(file.program, VXDUMP) == 0) { + if (strcmp(ctl_data->file.program, VXDUMP) == 0) { cmd = stralloc(VXRESTORE); } #endif #if defined(XFSDUMP) - if (strcmp(file.program, XFSDUMP) == 0) { + if (strcmp(ctl_data->file.program, XFSDUMP) == 0) { cmd = stralloc(XFSRESTORE); } #endif if (cmd == NULL) { - fprintf(stderr, "warning: restore program for %s not available.\n", - file.program); + g_fprintf(stderr, _("warning: restore program for %s not available.\n"), + ctl_data->file.program); cmd = stralloc("restore"); } + break; + case IS_APPLICATION_API: + cmd = vstralloc(APPLICATION_DIR, "/", ctl_data->file.application, NULL); + break; } if (cmd) { - dbprintf(("Exec'ing %s with arguments:\n", cmd)); - for (i = 0; i < j; i++) { - if( i == passwd_field) - dbprintf(("\tXXXXX\n")); + dbprintf(_("Exec'ing %s with arguments:\n"), cmd); + for (j = 0; j < argv_ptr->len - 1; j++) { + if (j == passwd_field) + dbprintf("\tXXXXX\n"); else - dbprintf(("\t%s\n", restore_args[i])); + dbprintf(_("\t%s\n"), (char *)g_ptr_array_index(argv_ptr, j)); } - (void)execv(cmd, restore_args); + safe_fd(-1, 0); + (void)execv(cmd, (char **)argv_ptr->pdata); /* only get here if exec failed */ save_errno = errno; - for (i = 0; i < j; i++) { - amfree(restore_args[i]); - } - amfree(restore_args); + g_ptr_array_free_full(argv_ptr); errno = save_errno; - perror("amrecover couldn't exec"); - fprintf(stderr, " problem executing %s\n", cmd); + perror(_("amrecover couldn't exec")); + g_fprintf(stderr, _(" problem executing %s\n"), cmd); amfree(cmd); } exit(1); @@ -1663,217 +2088,135 @@ static void extract_files_child(in_fd, elist) * some extraction program, really) and the socket from which we're reading, so * that we can do things like prompt for human interaction for multiple tapes. */ -void writer_intermediary(ctl_fd, data_fd, elist) - int ctl_fd, data_fd; - EXTRACT_LIST *elist; +int +writer_intermediary( + EXTRACT_LIST * elist) { - int child_pipe[2]; - pid_t pid; - char buffer[DISK_BLOCK_BYTES]; - ssize_t s; - ssize_t bytes_read; - amwait_t extractor_status; - int max_fd, nfound; - fd_set readset, selectset; - struct timeval timeout; - - /* - * If there's no distinct data channel (such as if we're talking to an - * older server), don't bother doing anything complicated. Just run the - * extraction. - */ - if(data_fd == -1){ - extract_files_child(ctl_fd, elist); - /* NOTREACHED */ - } - - if(pipe(child_pipe) == -1) { - error("extract_list - error setting up pipe to extractor: %s\n", - strerror(errno)); - /* NOTREACHED */ - } - - /* okay, ready to extract. fork a child to do the actual work */ - if ((pid = fork()) == 0) { - /* this is the child process */ - /* never gets out of this clause */ - aclose(child_pipe[1]); - extract_files_child(child_pipe[0], elist); - /* NOTREACHED */ - } - - /* This is the parent */ - if (pid == -1) { - error("writer_intermediary - error forking child"); - /* NOTREACHED */ - } - - aclose(child_pipe[0]); - - if(data_fd > ctl_fd) max_fd = data_fd+1; - else max_fd = ctl_fd+1; - FD_ZERO(&readset); - FD_SET(data_fd, &readset); - FD_SET(ctl_fd, &readset); - - do { - timeout.tv_sec = READ_TIMEOUT; - timeout.tv_usec = 0; - FD_COPY(&readset, &selectset); - - nfound = select(max_fd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL, - &timeout); - if(nfound < 0) { - fprintf(stderr,"select error: %s\n", strerror(errno)); - break; - } - - if (nfound == 0) { /* timeout */ - fprintf(stderr, "timeout waiting %d seconds for restore\n", - READ_TIMEOUT); - fprintf(stderr, "increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n"); - break; - } - - if(FD_ISSET(ctl_fd, &selectset)) { - bytes_read = read(ctl_fd, buffer, sizeof(buffer)-1); - switch(bytes_read) { - case -1: - if ((errno != EINTR) && (errno != EAGAIN)) { - if (errno != EPIPE) { - fprintf(stderr,"writer ctl fd read error: %s", - strerror(errno)); - } - FD_CLR(ctl_fd, &readset); - } - break; - - case 0: - FD_CLR(ctl_fd, &readset); - break; - - default: { - char desired_tape[MAX_TAPE_LABEL_BUF]; - - buffer[bytes_read] = '\0'; - /* if prompted for a tape, relay said prompt to the user */ - if(sscanf(buffer, "FEEDME %s\n", desired_tape) == 1) { - int done = 0; - while (!done) { - char *input = NULL; - printf("Please insert tape %s. Continue? [Y|n]: ", - desired_tape); - fflush(stdout); - - input = agets(stdin); /* strips \n */ - if (strcasecmp("", input) == 0|| - strcasecmp("y", input) == 0|| - strcasecmp("yes", input) == 0) { - send_to_tape_server(tape_control_sock, "OK"); - done = 1; - } else if (strcasecmp("n", input) == 0|| - strcasecmp("no", input) == 0) { - send_to_tape_server(tape_control_sock, "ERROR"); - /* Abort! - We are the middle process, so just die. */ - exit(EXIT_FAILURE); - } - amfree(input); - } - } else { - fprintf(stderr, "Strange message from tape server: %s", buffer); - - break; + ctl_data_t ctl_data; + amwait_t extractor_status; + + ctl_data.header_done = 0; + ctl_data.child_pipe[0] = -1; + ctl_data.child_pipe[1] = -1; + ctl_data.pid = -1; + ctl_data.elist = elist; + fh_init(&ctl_data.file); + ctl_data.data_path = DATA_PATH_AMANDA; + ctl_data.addrs = NULL; + ctl_data.bsu = NULL; + ctl_data.bytes_read = 0; + + security_stream_read(amidxtaped_streams[DATAFD].fd, + read_amidxtaped_data, &ctl_data); + + while(get_amidxtaped_line() >= 0) { + char desired_tape[MAX_TAPE_LABEL_BUF]; + g_debug("get amidxtaped line: %s", amidxtaped_line); + + /* if prompted for a tape, relay said prompt to the user */ + if(sscanf(amidxtaped_line, "FEEDME %132s\n", desired_tape) == 1) { + int done; + g_printf(_("Load tape %s now\n"), desired_tape); + dbprintf(_("Requesting tape %s from user\n"), desired_tape); + done = okay_to_continue(am_has_feature(indexsrv_features, + fe_amrecover_feedme_tape), + 0, 0); + if (done == 1) { + if (am_has_feature(indexsrv_features, + fe_amrecover_feedme_tape)) { + char *reply = stralloc2("TAPE ", tape_device_name); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, reply); + amfree(reply); + } else { + send_to_tape_server(amidxtaped_streams[CTLFD].fd, "OK"); } + } else { + send_to_tape_server(amidxtaped_streams[CTLFD].fd, "ERROR"); + break; } - } - } - - /* now read some dump data */ - if(FD_ISSET(data_fd, &selectset)) { - bytes_read = read(data_fd, buffer, sizeof(buffer)-1); - switch(bytes_read) { - case -1: - if ((errno != EINTR) && (errno != EAGAIN)) { - if (errno != EPIPE) { - fprintf(stderr,"writer data fd read error: %s", - strerror(errno)); - } - FD_CLR(data_fd, &readset); - } - break; - - case 0: - FD_CLR(data_fd, &readset); - break; - - default: - /* - * spit what we got from the server to the child - * process handling actual dumpfile extraction - */ - if((s = fullwrite(child_pipe[1], buffer, bytes_read)) < 0){ - if(errno == EPIPE) { - error("%s: pipe data reader has quit: %s\n", - get_pname(), strerror(errno)); - /* NOTREACHED */ - } - error("Write error to extract child: %s\n", - strerror(errno)); - /* NOTREACHED */ - } - break; + } else if (strncmp_const(amidxtaped_line, "USE-DATAPATH ") == 0) { + if (strncmp_const(amidxtaped_line+13, "AMANDA") == 0) { + ctl_data.data_path = DATA_PATH_AMANDA; + g_debug("Using AMANDA data-path"); + } else if (strncmp_const(amidxtaped_line+13, "DIRECT-TCP") == 0) { + ctl_data.data_path = DATA_PATH_DIRECTTCP; + ctl_data.addrs = stralloc(amidxtaped_line+24); + g_debug("Using DIRECT-TCP data-path with %s", ctl_data.addrs); } + start_processing_data(&ctl_data); + } else if(strncmp_const(amidxtaped_line, "MESSAGE ") == 0) { + g_printf("%s\n",&amidxtaped_line[8]); + } else { + g_fprintf(stderr, _("Strange message from tape server: %s"), + amidxtaped_line); + break; } - } while(FD_ISSET(ctl_fd, &readset) || FD_ISSET(data_fd, &readset)); + } - aclose(child_pipe[1]); + /* CTL might be close before DATA */ + event_loop(0); + dumpfile_free_data(&ctl_data.file); + amfree(ctl_data.addrs); + amfree(ctl_data.bsu); + if (ctl_data.child_pipe[1] != -1) + aclose(ctl_data.child_pipe[1]); - waitpid(pid, &extractor_status, 0); - if(WEXITSTATUS(extractor_status) != 0){ - int ret = WEXITSTATUS(extractor_status); - if(ret == 255) ret = -1; - error("Extractor child exited with status %d\n", ret); - /* NOTREACHED */ + if (ctl_data.header_done == 0) { + g_printf(_("Got no header and data from server, check in amidxtaped.*.debug and amandad.*.debug files on server\n")); } - exit(0); + if (ctl_data.pid != -1) { + waitpid(ctl_data.pid, &extractor_status, 0); + if(WEXITSTATUS(extractor_status) != 0){ + int ret = WEXITSTATUS(extractor_status); + if(ret == 255) ret = -1; + g_printf(_("Extractor child exited with status %d\n"), ret); + return -1; + } + } + g_debug("bytes read: %jd", (intmax_t)ctl_data.bytes_read); + return(0); } /* exec restore to do the actual restoration */ /* does the actual extraction of files */ -/* The original design had the dump image being returned exactly as it - appears on the tape, and this routine getting from the index server - whether or not it is compressed, on the assumption that the tape - server may not know how to uncompress it. But - - Amrestore can't do that. It returns either compressed or uncompressed - (always). Amrestore assumes it can uncompress files. It is thus a good - idea to run the tape server on a machine with gzip. - - The information about compression in the disklist is really only - for future dumps. It is possible to change compression on a drive - so the information in the disklist may not necessarily relate to - the dump image on the tape. - Consequently the design was changed to assuming that amrestore can - uncompress any dump image and have it return an uncompressed file - always. */ -void extract_files P((void)) +/* + * The original design had the dump image being returned exactly as it + * appears on the tape, and this routine getting from the index server + * whether or not it is compressed, on the assumption that the tape + * server may not know how to uncompress it. But + * - Amrestore can't do that. It returns either compressed or uncompressed + * (always). Amrestore assumes it can uncompress files. It is thus a good + * idea to run the tape server on a machine with gzip. + * - The information about compression in the disklist is really only + * for future dumps. It is possible to change compression on a drive + * so the information in the disklist may not necessarily relate to + * the dump image on the tape. + * Consequently the design was changed to assuming that amrestore can + * uncompress any dump image and have it return an uncompressed file + * always. + */ +void +extract_files(void) { EXTRACT_LIST *elist; - pid_t pid; - amwait_t child_stat; - char buf[STR_SIZE]; char *l; int first; int otc; - tapelist_t *tlist = NULL; + tapelist_t *tlist = NULL, *a_tlist; + g_option_t g_options; + levellist_t all_level = NULL; + int last_level; if (!is_extract_list_nonempty()) { - printf("Extract list empty - No files to extract!\n"); + g_printf(_("Extract list empty - No files to extract!\n")); return; } + clean_extract_list(); + /* get tape device name from index server if none specified */ if (tape_server_name == NULL) { tape_server_name = newstralloc(tape_server_name, server_name); @@ -1886,7 +2229,7 @@ void extract_files P((void)) l = reply_line(); if (!server_happy()) { - printf("%s\n", l); + g_printf("%s\n", l); exit(1); } /* skip reply number */ @@ -1895,72 +2238,82 @@ void extract_files P((void)) if (strcmp(tape_device_name, "/dev/null") == 0) { - printf("amrecover: warning: using %s as the tape device will not work\n", + g_printf(_("amrecover: warning: using %s as the tape device will not work\n"), tape_device_name); } first=1; - for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) + for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) { if(elist->tape[0]!='/') { if(first) { - printf("\nExtracting files using tape drive %s on host %s.\n", + g_printf(_("\nExtracting files using tape drive %s on host %s.\n"), tape_device_name, tape_server_name); - printf("The following tapes are needed:"); + g_printf(_("The following tapes are needed:")); first=0; } else - printf(" "); - tlist = unmarshal_tapelist_str(elist->tape); - for( ; tlist != NULL; tlist = tlist->next) - printf(" %s", tlist->label); - printf("\n"); - amfree(tlist); + g_printf(" "); + tlist = unmarshal_tapelist_str(elist->tape); + for(a_tlist = tlist ; a_tlist != NULL; a_tlist = a_tlist->next) + g_printf(" %s", a_tlist->label); + g_printf("\n"); + free_tapelist(tlist); } + } first=1; - for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) + for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist)) { if(elist->tape[0]=='/') { if(first) { - printf("\nExtracting files from holding disk on host %s.\n", + g_printf(_("\nExtracting files from holding disk on host %s.\n"), tape_server_name); - printf("The following files are needed:"); + g_printf(_("The following files are needed:")); first=0; } else - printf(" "); - tlist = unmarshal_tapelist_str(elist->tape); - for( ; tlist != NULL; tlist = tlist->next) - printf(" %s", tlist->label); - printf("\n"); - amfree(tlist); - } - printf("\n"); - getcwd(buf, sizeof(buf)); - printf("Restoring files into directory %s\n", buf); -#ifdef SAMBA_CLIENT - if (samba_extract_method == SAMBA_SMBCLIENT) - printf("(unless it is a Samba backup, that will go through to the SMB server)\n"); -#endif - if (!okay_to_continue(0,0,0)) - return; - printf("\n"); - + g_printf(" "); + tlist = unmarshal_tapelist_str(elist->tape); + for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next) + g_printf(" %s", a_tlist->label); + g_printf("\n"); + free_tapelist(tlist); + } + } + g_printf("\n"); + + g_options.config = get_config_name(); + g_options.hostname = dump_hostname; + for (elist = first_tape_list(); elist != NULL; + elist = next_tape_list(elist)) { + level_t *level = g_new0(level_t, 1); + level->level = elist->level; + all_level = g_slist_append(all_level, level); + } + if (dump_dle) { + g_slist_free_full(dump_dle->levellist); + dump_dle->levellist = all_level; + run_client_scripts(EXECUTE_ON_PRE_RECOVER, &g_options, dump_dle, + stderr); + dump_dle->levellist = NULL; + } + last_level = -1; while ((elist = first_tape_list()) != NULL) { if(elist->tape[0]=='/') { dump_device_name = newstralloc(dump_device_name, elist->tape); - printf("Extracting from file "); - tlist = unmarshal_tapelist_str(dump_device_name); - for( ; tlist != NULL; tlist = tlist->next) - printf(" %s", tlist->label); - printf("\n"); - amfree(tlist); + g_printf(_("Extracting from file ")); + tlist = unmarshal_tapelist_str(dump_device_name); + for(a_tlist = tlist; a_tlist != NULL; a_tlist = a_tlist->next) + g_printf(" %s", a_tlist->label); + g_printf("\n"); + free_tapelist(tlist); } else { - printf("Extracting files using tape drive %s on host %s.\n", + g_printf(_("Extracting files using tape drive %s on host %s.\n"), tape_device_name, tape_server_name); - tlist = unmarshal_tapelist_str(elist->tape); - printf("Load tape %s now\n", tlist->label); - amfree(tlist); + tlist = unmarshal_tapelist_str(elist->tape); + g_printf(_("Load tape %s now\n"), tlist->label); + dbprintf(_("Requesting tape %s from user\n"), tlist->label); + free_tapelist(tlist); otc = okay_to_continue(1,1,0); if (otc == 0) return; @@ -1972,67 +2325,524 @@ void extract_files P((void)) } dump_datestamp = newstralloc(dump_datestamp, elist->date); + if (last_level != -1 && dump_dle) { + level_t *level; + + level = g_new0(level_t, 1); + level->level = last_level; + dump_dle->levellist = g_slist_append(dump_dle->levellist, level); + + level = g_new0(level_t, 1); + level->level = elist->level; + dump_dle->levellist = g_slist_append(dump_dle->levellist, level); + run_client_scripts(EXECUTE_ON_INTER_LEVEL_RECOVER, &g_options, + dump_dle, stderr); + g_slist_free_full(dump_dle->levellist); + dump_dle->levellist = NULL; + } + /* connect to the tape handler daemon on the tape drive server */ - if ((tape_control_sock = extract_files_setup(elist->tape, elist->fileno)) == -1) + if ((extract_files_setup(elist->tape, elist->fileno)) == -1) { - fprintf(stderr, "amrecover - can't talk to tape server\n"); + g_fprintf(stderr, _("amrecover - can't talk to tape server: %s\n"), + errstr); return; } + if (dump_dle) { + level_t *level; + + level = g_new0(level_t, 1); + level->level = elist->level; + dump_dle->levellist = g_slist_append(dump_dle->levellist, level); + run_client_scripts(EXECUTE_ON_PRE_LEVEL_RECOVER, &g_options, + dump_dle, stderr); + } + last_level = elist->level; - /* okay, ready to extract. fork a child to do the actual work */ - if ((pid = fork()) == 0) - { - /* this is the child process */ - /* never gets out of this clause */ - writer_intermediary(tape_control_sock, tape_data_sock, elist); - /*NOT REACHED*/ + /* if the server have fe_amrecover_feedme_tape, it has asked for + * the tape itself, even if the restore didn't succeed, we should + * remove it. + */ + if(writer_intermediary(elist) == 0 || + am_has_feature(indexsrv_features, fe_amrecover_feedme_tape)) + delete_tape_list(elist); /* tape done so delete from list */ + + am_release_feature_set(tapesrv_features); + stop_amidxtaped(); + + if (dump_dle) { + run_client_scripts(EXECUTE_ON_POST_LEVEL_RECOVER, &g_options, + dump_dle, stderr); + g_slist_free_full(dump_dle->levellist); + dump_dle->levellist = NULL; } - /* this is the parent */ - if (pid == -1) - { - perror("extract_list - error forking child"); - exit(1); + } + if (dump_dle) { + dump_dle->levellist = all_level; + run_client_scripts(EXECUTE_ON_POST_RECOVER, &g_options, dump_dle, + stderr); + g_slist_free_full(dump_dle->levellist); + all_level = NULL; + dump_dle->levellist = NULL; + } +} + +static void +amidxtaped_response( + void * datap, + pkt_t * pkt, + security_handle_t * sech) +{ + int ports[NSTREAMS], *response_error = datap, i; + char *p; + char *tok; + char *extra = NULL; + + assert(response_error != NULL); + assert(sech != NULL); + memset(ports, -1, SIZEOF(ports)); + + if (pkt == NULL) { + errstr = newvstrallocf(errstr, _("[request failed: %s]"), security_geterror(sech)); + *response_error = 1; + return; + } + security_close_connection(sech, dump_hostname); + + if (pkt->type == P_NAK) { +#if defined(PACKET_DEBUG) + g_fprintf(stderr, _("got nak response:\n----\n%s\n----\n\n"), pkt->body); +#endif + + tok = strtok(pkt->body, " "); + if (tok == NULL || strcmp(tok, "ERROR") != 0) + goto bad_nak; + + tok = strtok(NULL, "\n"); + if (tok != NULL) { + errstr = newvstralloc(errstr, "NAK: ", tok, NULL); + *response_error = 1; + } else { +bad_nak: + errstr = newstralloc(errstr, "request NAK"); + *response_error = 2; } + return; + } - /* store the child pid globally so that it can be killed on intr */ - extract_restore_child_pid = pid; + if (pkt->type != P_REP) { + errstr = newvstrallocf(errstr, _("received strange packet type %s: %s"), + pkt_type2str(pkt->type), pkt->body); + *response_error = 1; + return; + } - /* wait for the child process to finish */ - if ((pid = waitpid(-1, &child_stat, 0)) == (pid_t)-1) - { - perror("extract_list - error waiting for child"); - exit(1); +#if defined(PACKET_DEBUG) + g_fprintf(stderr, _("got response:\n----\n%s\n----\n\n"), pkt->body); +#endif + + for(i = 0; i < NSTREAMS; i++) { + ports[i] = -1; + amidxtaped_streams[i].fd = NULL; + } + + p = pkt->body; + while((tok = strtok(p, " \n")) != NULL) { + p = NULL; + + /* + * Error response packets have "ERROR" followed by the error message + * followed by a newline. + */ + if (strcmp(tok, "ERROR") == 0) { + tok = strtok(NULL, "\n"); + if (tok == NULL) + tok = _("[bogus error packet]"); + errstr = newstralloc(errstr, tok); + *response_error = 2; + return; } - if(tape_data_sock != -1) { - aclose(tape_data_sock); + + /* + * Regular packets have CONNECT followed by three streams + */ + if (strcmp(tok, "CONNECT") == 0) { + + /* + * Parse the three stream specifiers out of the packet. + */ + for (i = 0; i < NSTREAMS; i++) { + tok = strtok(NULL, " "); + if (tok == NULL || strcmp(tok, amidxtaped_streams[i].name) != 0) { + extra = vstrallocf(_("CONNECT token is \"%s\": expected \"%s\""), + tok ? tok : "(null)", + amidxtaped_streams[i].name); + goto parse_error; + } + tok = strtok(NULL, " \n"); + if (tok == NULL || sscanf(tok, "%d", &ports[i]) != 1) { + extra = vstrallocf(_("CONNECT %s token is \"%s\": expected a port number"), + amidxtaped_streams[i].name, + tok ? tok : "(null)"); + goto parse_error; + } + } + continue; } - if (pid == extract_restore_child_pid) - { - extract_restore_child_pid = -1; + /* + * OPTIONS [options string] '\n' + */ + if (strcmp(tok, "OPTIONS") == 0) { + tok = strtok(NULL, "\n"); + if (tok == NULL) { + extra = stralloc(_("OPTIONS token is missing")); + goto parse_error; + } +/* + while((p = strchr(tok, ';')) != NULL) { + *p++ = '\0'; + if(strncmp_const(tok, "features=") == 0) { + tok += sizeof("features=") - 1; + am_release_feature_set(their_features); + if((their_features = am_string_to_feature(tok)) == NULL) { + errstr = newvstralloc(errstr, + _("OPTIONS: bad features value: "), + tok, + NULL); + goto parse_error; + } + } + tok = p; + } +*/ + continue; } - else - { - fprintf(stderr, "extract list - unknown child terminated?\n"); - exit(1); +/* + extra = vstrallocf("next token is \"%s\": expected \"CONNECT\", \"ERROR\" or \"OPTIONS\""), + tok ? tok : _("(null)")); + goto parse_error; +*/ + } + + /* + * Connect the streams to their remote ports + */ + for (i = 0; i < NSTREAMS; i++) { + if (ports[i] == -1) + continue; + amidxtaped_streams[i].fd = security_stream_client(sech, ports[i]); + dbprintf(_("amidxtaped_streams[%d].fd = %p\n"),i, amidxtaped_streams[i].fd); + if (amidxtaped_streams[i].fd == NULL) { + errstr = newvstrallocf(errstr,\ + _("[could not connect %s stream: %s]"), + amidxtaped_streams[i].name, + security_geterror(sech)); + goto connect_error; } - if ((WIFEXITED(child_stat) != 0) && (WEXITSTATUS(child_stat) != 0)) - { - fprintf(stderr, - "extract_list - child returned non-zero status: %d\n", - WEXITSTATUS(child_stat)); - otc = okay_to_continue(0,0,1); - if(otc == 0) - return; - else if(otc == 1) { - delete_tape_list(elist); /* tape failed so delete from list */ + } + /* + * Authenticate the streams + */ + for (i = 0; i < NSTREAMS; i++) { + if (amidxtaped_streams[i].fd == NULL) + continue; + if (security_stream_auth(amidxtaped_streams[i].fd) < 0) { + errstr = newvstrallocf(errstr, + _("[could not authenticate %s stream: %s]"), + amidxtaped_streams[i].name, + security_stream_geterror(amidxtaped_streams[i].fd)); + goto connect_error; + } + } + + /* + * The CTLFD and DATAFD streams are mandatory. If we didn't get + * them, complain. + */ + if (amidxtaped_streams[CTLFD].fd == NULL) { + errstr = newvstrallocf(errstr, _("[couldn't open CTL streams]")); + goto connect_error; + } + if (amidxtaped_streams[DATAFD].fd == NULL) { + errstr = newvstrallocf(errstr, _("[couldn't open DATA streams]")); + goto connect_error; + } + + /* everything worked */ + *response_error = 0; + return; + +parse_error: + if (extra) { + errstr = newvstrallocf(errstr, + _("[parse of reply message failed: %s]"), extra); + } else { + errstr = newvstrallocf(errstr, + _("[parse of reply message failed: (no additional information)")); + } + amfree(extra); + *response_error = 2; + return; + +connect_error: + stop_amidxtaped(); + *response_error = 1; +} + +/* + * This is called when everything needs to shut down so event_loop() + * will exit. + */ +static void +stop_amidxtaped(void) +{ + int i; + + for (i = 0; i < NSTREAMS; i++) { + if (amidxtaped_streams[i].fd != NULL) { + security_stream_close(amidxtaped_streams[i].fd); + amidxtaped_streams[i].fd = NULL; + } + } +} + +static char* ctl_buffer = NULL; +/* gets a "line" from server and put in server_line */ +/* server_line is terminated with \0, \r\n is striped */ +/* returns -1 if error */ + +int +get_amidxtaped_line(void) +{ + ssize_t size; + char *newbuf, *s; + void *buf; + + amfree(amidxtaped_line); + if (!ctl_buffer) + ctl_buffer = stralloc(""); + + while (!strstr(ctl_buffer,"\r\n")) { + if (amidxtaped_streams[CTLFD].fd == NULL) + return -1; + + size = security_stream_read_sync(amidxtaped_streams[CTLFD].fd, &buf); + if(size < 0) { + return -1; + } + else if(size == 0) { + return -1; + } + newbuf = alloc(strlen(ctl_buffer)+size+1); + strncpy(newbuf, ctl_buffer, (size_t)(strlen(ctl_buffer) + size + 1)); + memcpy(newbuf+strlen(ctl_buffer), buf, (size_t)size); + newbuf[strlen(ctl_buffer)+size] = '\0'; + amfree(ctl_buffer); + ctl_buffer = newbuf; + amfree(buf); + } + + s = strstr(ctl_buffer,"\r\n"); + *s = '\0'; + newbuf = stralloc(s+2); + amidxtaped_line = stralloc(ctl_buffer); + amfree(ctl_buffer); + ctl_buffer = newbuf; + return 0; +} + + +static void +read_amidxtaped_data( + void * cookie, + void * buf, + ssize_t size) +{ + ctl_data_t *ctl_data = (ctl_data_t *)cookie; + assert(cookie != NULL); + + if (size < 0) { + errstr = newstralloc2(errstr, _("amidxtaped read: "), + security_stream_geterror(amidxtaped_streams[DATAFD].fd)); + return; + } + + /* + * EOF. Stop and return. + */ + if (size == 0) { + security_stream_close(amidxtaped_streams[DATAFD].fd); + amidxtaped_streams[DATAFD].fd = NULL; + /* + * If the mesg fd has also shut down, then we're done. + */ + return; + } + + assert(buf != NULL); + + if (ctl_data->header_done == 0) { + GPtrArray *errarray; + g_option_t g_options; + data_path_t data_path_set = DATA_PATH_AMANDA; + + /* parse the file header */ + fh_init(&ctl_data->file); + parse_file_header(buf, &ctl_data->file, (size_t)size); + + /* call backup_support_option */ + g_options.config = get_config_name(); + g_options.hostname = dump_hostname; + if (strcmp(ctl_data->file.program, "APPLICATION") == 0) { + if (dump_dle) { + ctl_data->bsu = backup_support_option(ctl_data->file.application, + &g_options, + ctl_data->file.disk, + dump_dle->device, + &errarray); + } else { + ctl_data->bsu = backup_support_option(ctl_data->file.application, + &g_options, + ctl_data->file.disk, NULL, + &errarray); } - else { /* RETRY_TAPE */ + if (!ctl_data->bsu) { + guint i; + for (i=0; i < errarray->len; i++) { + char *line; + line = g_ptr_array_index(errarray, i); + g_fprintf(stderr, "%s\n", line); + } + g_ptr_array_free_full(errarray); + exit(1); } + data_path_set = ctl_data->bsu->data_path_set; } - else { - delete_tape_list(elist); /* tape done so delete from list */ + /* handle backup_support_option failure */ + + ctl_data->header_done = 1; + if (!ask_file_overwrite(ctl_data)) { + if (am_has_feature(tapesrv_features, fe_amidxtaped_abort)) { + send_to_tape_server(amidxtaped_streams[CTLFD].fd, "ABORT"); + } + stop_amidxtaped(); + return; + } + + if (am_has_feature(tapesrv_features, fe_amidxtaped_datapath)) { + char *msg; + /* send DATA-PATH request */ + msg = stralloc("AVAIL-DATAPATH"); + if (data_path_set & DATA_PATH_AMANDA) + vstrextend(&msg, " AMANDA", NULL); + if (data_path_set & DATA_PATH_DIRECTTCP) + vstrextend(&msg, " DIRECT-TCP", NULL); + send_to_tape_server(amidxtaped_streams[CTLFD].fd, msg); + amfree(msg); + } else { + start_processing_data(ctl_data); + } + } else { + ctl_data->bytes_read += size; + /* Only the data is sent to the child */ + /* + * We ignore errors while writing to the index file. + */ + (void)full_write(ctl_data->child_pipe[1], buf, (size_t)size); + security_stream_read(amidxtaped_streams[DATAFD].fd, + read_amidxtaped_data, cookie); + } +} + +static gboolean +ask_file_overwrite( + ctl_data_t *ctl_data) +{ + char *restore_dir = NULL; + + if (ctl_data->file.dumplevel == 0) { + property_t *property = g_hash_table_lookup(proplist, "directory"); + if (property && property->values && property->values->data) { + /* take first property value */ + restore_dir = strdup(property->values->data); + } + if (samba_extract_method == SAMBA_SMBCLIENT || + (ctl_data->bsu && + ctl_data->bsu->recover_path == RECOVER_PATH_REMOTE)) { + if (!restore_dir) { + restore_dir = g_strdup(ctl_data->file.disk); + } + g_printf(_("Restoring files into target host %s\n"), restore_dir); + } else { + if (!restore_dir) { + restore_dir = g_get_current_dir(); + } + g_printf(_("Restoring files into directory %s\n"), restore_dir); + } + + /* Collect files to delete befause of a bug in gnutar */ + if (strcmp(ctl_data->file.program, "GNUTAR") == 0 || + (strcmp(ctl_data->file.program, "APPLICATION") == 0 && + strcmp(ctl_data->file.application, "amgtar") == 0)) { + check_file_overwrite(restore_dir); + } else { + g_printf(_("All existing files in %s can be deleted\n"), + restore_dir); + } + + if (!okay_to_continue(0,0,0)) { + free_unlink_list(); + amfree(restore_dir); + return FALSE; + } + g_printf("\n"); + + /* delete the files for gnutar */ + if (unlink_list) { + if (!do_unlink_list()) { + g_fprintf(stderr, _("Can't recover because I can't cleanup the restore directory (%s)\n"), + restore_dir); + free_unlink_list(); + amfree(restore_dir); + return FALSE; + } + free_unlink_list(); } + amfree(restore_dir); + } + return TRUE; +} + +static void +start_processing_data( + ctl_data_t *ctl_data) +{ + if (pipe(ctl_data->child_pipe) == -1) { + error(_("extract_list - error setting up pipe to extractor: %s\n"), + strerror(errno)); + /*NOTREACHED*/ + } + + /* okay, ready to extract. fork a child to do the actual work */ + if ((ctl_data->pid = fork()) == 0) { + /* this is the child process */ + /* never gets out of this clause */ + aclose(ctl_data->child_pipe[1]); + extract_files_child(ctl_data); + /*NOTREACHED*/ + } + + if (ctl_data->pid == -1) { + errstr = newstralloc(errstr, _("writer_intermediary - error forking child")); + g_printf(_("writer_intermediary - error forking child")); + return; + } + aclose(ctl_data->child_pipe[0]); + security_stream_read(amidxtaped_streams[DATAFD].fd, read_amidxtaped_data, + ctl_data); + if (am_has_feature(tapesrv_features, fe_amidxtaped_datapath)) { + send_to_tape_server(amidxtaped_streams[CTLFD].fd, "DATAPATH-OK"); } }