* file named AUTHORS, in the root directory of this distribution.
*/
/*
- * $Id: extract_list.c,v 1.43.2.13.4.6.2.19 2003/03/04 21:13:58 martinea Exp $
+ * $Id: extract_list.c,v 1.117 2006/08/24 01:57:15 paddy_s Exp $
*
* implements the "extract" command in amrecover
*/
#include "fileheader.h"
#include "dgram.h"
#include "stream.h"
+#include "tapelist.h"
#ifdef SAMBA_CLIENT
#include "findpass.h"
#endif
-
-#if defined(KRB4_SECURITY)
-#include "krb4-security.h"
-#endif
#include "util.h"
-
-typedef struct EXTRACT_LIST_ITEM
-{
- char path[1024];
-
+#include "conffile.h"
+#include "protocol.h"
+#include "event.h"
+#include "client_util.h"
+#include "security.h"
+
+typedef struct EXTRACT_LIST_ITEM {
+ char *path;
struct EXTRACT_LIST_ITEM *next;
}
EXTRACT_LIST_ITEM;
-typedef struct EXTRACT_LIST
-{
- char date[11]; /* date tape created */
- int level; /* level of dump */
- char tape[256]; /* tape label */
- int fileno; /* fileno on tape */
- EXTRACT_LIST_ITEM *files; /* files to get off tape */
+typedef struct EXTRACT_LIST {
+ char *date; /* date tape created */
+ int level; /* level of dump */
+ char *tape; /* tape label */
+ off_t fileno; /* fileno on tape */
+ EXTRACT_LIST_ITEM *files; /* files to get off tape */
struct EXTRACT_LIST *next;
}
#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);
+char *amidxtaped_client_get_security_conf(char *, 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 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));
+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(int in_fd, EXTRACT_LIST *elist);
+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);
+
+/*
+ * Function: ssize_t read_buffer(datafd, buffer, buflen, timeout_s)
+ *
+ * Description:
+ * read data from input file desciptor waiting up to timeout_s
+ * seconds before returning data.
+ *
+ * Inputs:
+ * datafd - File descriptor to read from.
+ * buffer - Buffer to read into.
+ * buflen - Maximum number of bytes to read into buffer.
+ * timeout_s - Seconds to wait before returning what was already read.
+ *
+ * Returns:
+ * >0 - Number of data bytes in buffer.
+ * 0 - EOF
+ * -1 - errno == ETIMEDOUT if no data available in specified time.
+ * errno == ENFILE if datafd is invalid.
+ * otherwise errno is set by select or read..
+ */
-ssize_t read_buffer(datafd, buffer, buflen)
-int datafd;
-char *buffer;
-size_t buflen;
+static ssize_t
+read_buffer(
+ int datafd,
+ char * buffer,
+ size_t buflen,
+ long timeout_s)
{
- int maxfd, nfound = 0;
ssize_t size = 0;
- fd_set readset, selectset;
+ SELECT_ARG_TYPE readset;
struct timeval timeout;
char *dataptr;
- size_t spaceleft;
- int eof;
+ 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;
-
- maxfd = datafd + 1;
- eof = 0;
-
- FD_ZERO(&readset);
- FD_SET(datafd, &readset);
+ spaceleft = (ssize_t)buflen;
do {
+ FD_ZERO(&readset);
+ FD_SET(datafd, &readset);
+ timeout.tv_sec = timeout_s;
+ timeout.tv_usec = 0;
+ nfound = select(datafd+1, &readset, NULL, NULL, &timeout);
+ if(nfound < 0 ) {
+ /* Select returned an error. */
+ g_fprintf(stderr,_("select error: %s\n"), strerror(errno));
+ size = -1;
+ break;
+ }
+
+ if (nfound == 0) {
+ /* Select timed out. */
+ if (timeout_s != 0) {
+ /* Not polling: a real read timeout */
+ 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;
+ break;
+ }
- timeout.tv_sec = READ_TIMEOUT;
- timeout.tv_usec = 0;
- memcpy(&selectset, &readset, sizeof(fd_set));
-
- nfound = select(maxfd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL, &timeout);
-
- /* check for errors or timeout */
-
- if(nfound == 0) {
- size=-2;
- fprintf(stderr,"timeout waiting for amrestore\n");
- fprintf(stderr,"increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n");
- }
- if(nfound == -1) {
- size=-3;
- fprintf(stderr,"nfound == -1\n");
- }
-
- /* read any data */
+ if(!FD_ISSET(datafd, &readset))
+ continue;
- if(FD_ISSET(datafd, &selectset)) {
- size = read(datafd, dataptr, spaceleft);
- switch(size) {
- case -1:
- break;
- case 0:
- spaceleft -= size;
- dataptr += size;
- fprintf(stderr,
- "EOF, check amidxtaped.<timestamp>.debug file on %s.\n",
- tape_server_name);
- break;
- default:
- spaceleft -= size;
- dataptr += size;
- break;
+ /* Select says data is available, so read it. */
+ size = read(datafd, dataptr, (size_t)spaceleft);
+ if (size < 0) {
+ if ((errno == EINTR) || (errno == EAGAIN)) {
+ continue;
}
+ if (errno != EPIPE) {
+ g_fprintf(stderr, _("read_buffer: read error - %s"),
+ strerror(errno));
+ break;
+ }
+ size = 0;
}
- } while (spaceleft>0 && size>0);
+ spaceleft -= size;
+ dataptr += size;
+ } while ((size > 0) && (spaceleft > 0));
- if(size<0) {
- return -1;
- }
- return (ssize_t)(buflen-spaceleft);
+ 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)
{
next = this->next;
- free(this);
+ amfree(this->path);
+ amfree(this);
this = next;
}
tape_list->files = NULL;
/* 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;
+ if (tape_list == NULL)
+ return;
+
/* is it first on the list? */
if (tape_list == extract_list)
{
- clear_tape_list(tape_list);
extract_list = tape_list->next;
+ clear_tape_list(tape_list);
+ amfree(tape_list->date);
+ amfree(tape_list->tape);
amfree(tape_list);
return;
}
{
if (this == tape_list)
{
- clear_tape_list(tape_list);
prev->next = tape_list->next;
+ clear_tape_list(tape_list);
+ amfree(tape_list->date);
+ amfree(tape_list->tape);
amfree(tape_list);
return;
}
/* 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;
}
-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;
+ }
+ }
+}
+
+
+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;
curr=curr->next;
}
that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
- strncpy(that->path, ditem_path, sizeof(that->path)-1);
- that->path[sizeof(that->path)-1] = '\0';
+ that->path = stralloc(ditem_path);
that->next = this->files;
this->files = that; /* add at front since easiest */
amfree(ditem_path);
/* so this is the first time we have seen this tape */
this = (EXTRACT_LIST *)alloc(sizeof(EXTRACT_LIST));
- strncpy(this->tape, ditem->tape, sizeof(this->tape)-1);
- this->tape[sizeof(this->tape)-1] ='\0';
+ this->tape = stralloc(ditem->tape);
this->level = ditem->level;
this->fileno = ditem->fileno;
- strncpy(this->date, ditem->date, sizeof(this->date)-1);
- this->date[sizeof(this->date)-1] = '\0';
+ this->date = stralloc(ditem->date);
that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
- strncpy(that->path, ditem_path, sizeof(that->path)-1);
- that->path[sizeof(that->path)-1] = '\0';
+ that->path = stralloc(ditem_path);
that->next = NULL;
this->files = that;
/* 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;
{
/* first on list */
this->files = that->next;
+ amfree(that->path);
amfree(that);
/* if list empty delete it */
if (this->files == NULL)
if (strcmp(that->path, ditem_path) == 0)
{
prev->next = that->next;
+ amfree(that->path);
amfree(that);
amfree(ditem_path);
return 0;
}
-void add_glob(glob)
-char *glob;
+void
+add_glob(
+ char * glob)
{
char *regex;
char *regex_path;
char *s;
-
- regex = glob_to_regex(glob);
- dbprintf(("add_glob (%s) -> %s\n", glob, regex));
+ char *uqglob = unquote_string(glob);
+
+ regex = glob_to_regex(uqglob);
+ dbprintf(_("add_glob (%s) -> %s\n"), uqglob, regex);
if ((s = validate_regexp(regex)) != NULL) {
- printf("\"%s\" is not a valid shell wildcard pattern: ", glob);
+ g_printf(_("%s is not a valid shell wildcard pattern: "), glob);
puts(s);
- return;
+ } 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);
}
- /*
- * 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);
+ amfree(uqglob);
}
-void add_regex(regex)
-char *regex;
+void
+add_regex(
+ char * regex)
{
char *s;
-
- if ((s = validate_regexp(regex)) != NULL) {
- printf("\"%s\" is not a valid regular expression: ", regex);
+ char *uqregex = unquote_string(regex);
+
+ if ((s = validate_regexp(uqregex)) != NULL) {
+ g_printf(_("\"%s\" is not a valid regular expression: "), regex);
puts(s);
- return;
+ } else {
+ add_file(uqregex, regex);
}
- add_file(regex, regex);
+ amfree(uqregex);
}
-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)
- path_on_disk = stralloc2("/", regex);
- else {
+ if (strcmp(disk_path, "/") == 0) {
+ if (*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);
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);
+ cmd = NULL;
/* skip preamble */
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;
}
- amfree(err);
dir_undo = NULL;
added=0;
- strncpy(lditem.path, ditem_path, sizeof(lditem.path)-1);
- lditem.path[sizeof(lditem.path)-1] = '\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) {
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";
- continue;
- }
- copy_string(s, ch, lditem.date, sizeof(lditem.date), fp);
- if(fp == NULL) {
- err = "bad reply: date field too large";
+ 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] = (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";
- continue;
- }
- copy_string(s, ch, lditem.tape, sizeof(lditem.tape), fp);
- if(fp == NULL) {
- err = "bad reply: tape field too large";
+ err = _("bad reply: missing tape field");
continue;
}
-
- if(am_has_feature(their_features, fe_amindexd_fileno_in_ORLD)) {
+ fp = s-1;
+ skip_non_whitespace(s, ch);
+ s[-1] = '\0';
+ lditem.tape = newstralloc(lditem.tape, 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;
}
if(!server_happy()) {
puts(reply_line());
} else if(err) {
- if(*err) {
+ if (*err)
puts(err);
- }
- puts(cmd);
+ 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);
}
}
}
}
+
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;
-
- regex = glob_to_regex(glob);
- dbprintf(("delete_glob (%s) -> %s\n", glob, regex));
+ char *uqglob = unquote_string(glob);
+
+ regex = glob_to_regex(uqglob);
+ dbprintf(_("delete_glob (%s) -> %s\n"), uqglob, regex);
if ((s = validate_regexp(regex)) != NULL) {
- printf("\"%s\" is not a valid shell wildcard pattern: ", glob);
+ g_printf(_("\"%s\" is not a valid shell wildcard pattern: "), glob);
puts(s);
- return;
+ } 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);
}
- /*
- * 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);
+ amfree(uqglob);
}
-void delete_regex(regex)
-char *regex;
+void
+delete_regex(
+ char * regex)
{
char *s;
+ char *uqregex = unquote_string(regex);
if ((s = validate_regexp(regex)) != NULL) {
- printf("\"%s\" is not a valid regular expression: ", regex);
+ g_printf(_("\"%s\" is not a valid regular expression: "), regex);
puts(s);
- return;
+ } else {
+ delete_file(uqregex, uqregex);
}
- delete_file(regex, regex);
+ amfree(uqregex);
}
-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 {
+ 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);
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);
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;
- strncpy(lditem.path, ditem->path, sizeof(lditem.path)-1);
- lditem.path[sizeof(lditem.path)-1] = '\0';
+ lditem.path = newstralloc(lditem.path, ditem->path);
amfree(cmd);
- amfree(err);
- 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 */
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;
tape_undo_ch = *tape_undo;
*tape_undo = '\0';
- if(am_has_feature(their_features, fe_amindexd_fileno_in_ORLD)) {
+ 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;
*dir_undo = '\0';
- strncpy(lditem.date, date, sizeof(lditem.date)-1);
- lditem.date[sizeof(lditem.date)-1] = '\0';
+ lditem.date = newstralloc(lditem.date, date);
lditem.level=level;
- strncpy(lditem.tape, tape, sizeof(lditem.tape)-1);
- lditem.tape[sizeof(lditem.tape)-1] = '\0';
+ 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:
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;
}
}
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)
{
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);
}
/* 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);
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')) {
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)
{
- size_t l, n;
- ssize_t s;
- char *end;
+ char *msg = stralloc2(cmd, "\r\n");
- for (l = 0, n = strlen(cmd); l < n; l += s)
- if ((s = write(tss, cmd + l, n - l)) < 0)
- {
- perror("Error writing to tape server");
- exit(101);
- }
- end = "\r\n";
- for (l = 0, n = strlen(end); l < n; l += s)
- if ((s = write(tss, end + l, n - l)) < 0)
- {
- perror("Error writing to tape server");
- exit(101);
- }
+ if (security_stream_write(stream, msg, strlen(msg)) < 0)
+ {
+ error(_("Error writing to tape server"));
+ exit(101);
+ /*NOTREACHED*/
+ }
+ amfree(msg);
}
/* 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;
- int tape_server_socket;
char *disk_regex = NULL;
char *host_regex = NULL;
- char *service_name = NULL;
- char *line = NULL;
char *clean_datestamp, *ch, *ch1;
-
- 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;
+ char *tt = NULL;
+ char *req;
+ int response_error;
+
+ amidxtaped_secdrv = security_getdriver(authopt);
+ if (amidxtaped_secdrv == NULL) {
+ error(_("no '%s' security driver available for host '%s'"),
+ authopt, tape_server_name);
}
- amfree(service_name);
- seteuid(0); /* it either works ... */
- setegid(0);
- tape_server_socket = stream_client_privileged(tape_server_name,
- ntohs(sp->s_port),
- -1,
- STREAM_BUFSIZE,
- &my_port);
- if (tape_server_socket < 0)
- {
- printf("cannot connect to %s: %s\n", tape_server_name, strerror(errno));
- return -1;
- }
- if (my_port >= IPPORT_RESERVED) {
- aclose(tape_server_socket);
- printf("did not get a reserved port: %d\n", my_port);
- return -1;
- }
- setegid(getgid());
- seteuid(getuid()); /* put it back */
- /* do the security thing */
-#if defined(KRB4_SECURITY)
-#if 0 /* not yet implemented */
- if(krb4_auth)
- {
- line = get_krb_security();
- }
-#endif /* 0 */
-#endif
- {
- line = get_bsd_security();
+ /* 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;
}
- send_to_tape_server(tape_server_socket, line);
- memset(line, '\0', strlen(line));
- amfree(line);
disk_regex = alloc(strlen(disk_name) * 2 + 3);
}
}
*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)){
+ 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;
+ }
+ am_release_feature_set(tapesrv_features);
+ }
- if(am_has_feature(their_features, fe_amidxtaped_header) &&
- am_has_feature(their_features, fe_amidxtaped_device) &&
- am_has_feature(their_features, fe_amidxtaped_host) &&
- am_has_feature(their_features, fe_amidxtaped_disk) &&
- am_has_feature(their_features, fe_amidxtaped_datestamp)) {
- char *tt = NULL;
+ if(am_has_feature(indexsrv_features, fe_amidxtaped_header) &&
+ am_has_feature(indexsrv_features, fe_amidxtaped_device) &&
+ am_has_feature(indexsrv_features, fe_amidxtaped_host) &&
+ am_has_feature(indexsrv_features, fe_amidxtaped_disk) &&
+ am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) {
- if(am_has_feature(their_features, fe_amidxtaped_config)) {
- tt = newstralloc2(tt, "CONFIG=", config);
- send_to_tape_server(tape_server_socket, tt);
+ if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) {
+ tt = newstralloc2(tt, "CONFIG=", get_config_name());
+ send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
}
- if(am_has_feature(their_features, fe_amidxtaped_label) &&
+ if(am_has_feature(indexsrv_features, fe_amidxtaped_label) &&
label && label[0] != '/') {
tt = newstralloc2(tt,"LABEL=",label);
- send_to_tape_server(tape_server_socket, tt);
+ send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
}
- if(am_has_feature(their_features, fe_amidxtaped_fsf)) {
+ if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) {
char v_fsf[100];
- ap_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_server_socket, tt);
+ send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
}
- send_to_tape_server(tape_server_socket, "HEADER");
+ send_to_tape_server(amidxtaped_streams[CTLFD].fd, "HEADER");
tt = newstralloc2(tt, "DEVICE=", dump_device_name);
- send_to_tape_server(tape_server_socket, tt);
+ send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
tt = newstralloc2(tt, "HOST=", host_regex);
- send_to_tape_server(tape_server_socket, tt);
+ send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
tt = newstralloc2(tt, "DISK=", disk_regex);
- send_to_tape_server(tape_server_socket, tt);
+ send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp);
- send_to_tape_server(tape_server_socket, tt);
- send_to_tape_server(tape_server_socket, "END");
+ send_to_tape_server(amidxtaped_streams[CTLFD].fd, tt);
+ send_to_tape_server(amidxtaped_streams[CTLFD].fd, "END");
amfree(tt);
}
- else if(1 /* am_has_feature(their_features, fe_amidxtaped_nargs) */) {
- /* 2.4.3 doesn't set fe_amidxtaped_nargs but support it */
- /* must be supported without test until 2005 */
-
+ else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) {
/* send to the tape server what tape file we want */
/* 6 args:
* "-h"
* "diskname"
* "datestamp"
*/
- send_to_tape_server(tape_server_socket, "6");
- send_to_tape_server(tape_server_socket, "-h");
- send_to_tape_server(tape_server_socket, "-p");
- send_to_tape_server(tape_server_socket, dump_device_name);
- send_to_tape_server(tape_server_socket, host_regex);
- send_to_tape_server(tape_server_socket, disk_regex);
- send_to_tape_server(tape_server_socket, clean_datestamp);
-
- dbprintf(("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n",
- dump_device_name, host_regex, disk_regex, clean_datestamp));
+ 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_server_socket;
+ return 0;
}
-size_t 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);
+ bytes_read = read_buffer(tapedev, buffer, buflen, READ_TIMEOUT);
if(bytes_read < 0) {
- error("error reading tape: %s", strerror(errno));
+ error(_("error reading header (%s), check amidxtaped.*.debug on server"),
+ strerror(errno));
+ /*NOTREACHED*/
}
- else if((size_t)bytes_read < buflen) {
- fprintf(stderr, "%s: short block %d byte%s\n",
- get_pname(), (int)bytes_read, (bytes_read == 1) ? "" : "s");
+
+ if((size_t)bytes_read < buflen) {
+ 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");
+ error(_("Can't read file header"));
+ /*NOTREACHED*/
}
- else { /* bytes_read == buflen */
- parse_file_header(buffer, file, bytes_read);
- }
- return((size_t)bytes_read);
-}
-enum dumptypes {IS_UNKNOWN, IS_DUMP, IS_GNUTAR, IS_TAR, IS_SAMBA, IS_SAMBA_TAR};
+ /* bytes_read == buflen */
+ parse_file_header(buffer, file, (size_t)bytes_read);
+}
-/* exec restore to do the actual restoration */
-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(
+ int in_fd,
+ EXTRACT_LIST * elist)
{
int save_errno;
int extra_params = 0;
enum dumptypes dumptype = IS_UNKNOWN;
char buffer[DISK_BLOCK_BYTES];
dumpfile_t file;
- size_t buflen;
size_t len_program;
char *cmd = NULL;
int passwd_field = -1;
#ifdef SAMBA_CLIENT
char *domain = NULL, *smbpass = NULL;
#endif
+ backup_support_option_t *bsu;
+ GPtrArray *errarray;
/* code executed by child to do extraction */
/* never returns */
/* make in_fd be our stdin */
if (dup2(in_fd, STDIN_FILENO) == -1)
{
- perror("extract_list - extract files client");
- exit(1);
+ error(_("dup2 failed in extract_files_child: %s"), strerror(errno));
+ /*NOTREACHED*/
}
/* read the file header */
fh_init(&file);
- buflen=read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO);
+ read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO);
- if(buflen == 0 || file.type != F_DUMPFILE) {
+ if(file.type != F_DUMPFILE) {
print_header(stdout, &file);
- error("bad header");
+ error(_("bad header"));
+ /*NOTREACHED*/
}
if (file.program != NULL) {
+ if (strcmp(file.program, "APPLICATION") == 0)
+ dumptype = IS_APPLICATION_API;
#ifdef GNUTAR
if (strcmp(file.program, GNUTAR) == 0)
dumptype = IS_GNUTAR;
#endif
case IS_TAR:
case IS_GNUTAR:
+ extra_params = 4;
+ break;
case IS_SAMBA_TAR:
extra_params = 3;
break;
}
#endif
break;
+ case IS_APPLICATION_API:
+ extra_params = 12;
+ if (dump_dle) {
+ GSList *scriptlist;
+ script_t *script;
+ extra_params += application_property_argv_size(dump_dle);
+ for (scriptlist = dump_dle->scriptlist; scriptlist != NULL;
+ scriptlist = scriptlist->next) {
+ script = (script_t *)scriptlist->data;
+ if (script->result && script->result->proplist) {
+ extra_params += property_argv_size(script->result->proplist);
+ }
+ }
+ }
+ break;
}
-
- restore_args = (char **)alloc((extra_params + files_off_tape + 1)
- * sizeof(char *));
+ restore_args = (char **)alloc((size_t)((extra_params + files_off_tape + 1)
+ * sizeof(char *)));
switch(dumptype) {
case IS_SAMBA:
#ifdef SAMBA_CLIENT
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 */
break;
restore_args[j++] = stralloc("-"); /* data on stdin */
}
#endif
+ break;
+ case IS_APPLICATION_API:
+ {
+ g_option_t g_options;
+ g_options.config = get_config_name();
+ g_options.hostname = dump_hostname;
+ if (dump_dle) {
+ bsu = backup_support_option(file.application, &g_options,
+ file.disk, dump_dle->device,
+ &errarray);
+ } else {
+ bsu = backup_support_option(file.application, &g_options,
+ file.disk, NULL,
+ &errarray);
+ }
+ }
+ restore_args[j++] = stralloc(file.application);
+ restore_args[j++] = stralloc("restore");
+ restore_args[j++] = stralloc("--config");
+ restore_args[j++] = stralloc(get_config_name());
+ restore_args[j++] = stralloc("--disk");
+ restore_args[j++] = stralloc(file.disk);
+ if (dump_dle && dump_dle->device) {
+ restore_args[j++] = stralloc("--device");
+ restore_args[j++] = stralloc(dump_dle->device);
+ }
+ if (bsu->smb_recover_mode && samba_extract_method == SAMBA_SMBCLIENT){
+ restore_args[j++] = "--recover-mode";
+ restore_args[j++] = "smb";
+ }
+ if (dump_dle) {
+ GSList *scriptlist;
+ script_t *script;
+
+ j += application_property_add_to_argv(&restore_args[j], dump_dle, NULL);
+ for (scriptlist = dump_dle->scriptlist; scriptlist != NULL;
+ scriptlist = scriptlist->next) {
+ script = (script_t *)scriptlist->data;
+ if (script->result && script->result->proplist) {
+ j += property_add_to_argv(&restore_args[j],
+ script->result->proplist);
+ }
+ }
+
+ }
+ break;
}
for (i = 0, fn = 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:
- restore_args[j++] = stralloc2(".", fn->path);
+ if (strcmp(fn->path, "/") == 0)
+ restore_args[j++] = stralloc(".");
+ else
+ restore_args[j++] = stralloc2(".", fn->path);
break;
case IS_UNKNOWN:
case IS_DUMP:
{
restore_args[j++] = stralloc(fn->path);
}
+ break;
}
}
#if defined(XFSDUMP)
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);
}
#endif
if (cmd == NULL) {
- fprintf(stderr, "warning: restore program for %s not available.\n",
+ g_fprintf(stderr, _("warning: restore program for %s not available.\n"),
file.program);
cmd = stralloc("restore");
}
+ break;
+ case IS_APPLICATION_API:
+ cmd = vstralloc(APPLICATION_DIR, "/", file.application, NULL);
+ break;
}
if (cmd) {
- dbprintf(("Exec'ing %s with arguments:\n", cmd));
+ dbprintf(_("Exec'ing %s with arguments:\n"), cmd);
for (i = 0; i < j; i++) {
if( i == passwd_field)
- dbprintf(("\tXXXXX\n"));
+ dbprintf("\tXXXXX\n");
else
- dbprintf(("\t%s\n", restore_args[i]));
+ dbprintf(_("\t%s\n"), restore_args[i]);
}
+ safe_fd(-1, 0);
(void)execv(cmd, restore_args);
/* only get here if exec failed */
save_errno = errno;
}
amfree(restore_args);
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);
/*NOT REACHED */
}
+typedef struct ctl_data_s {
+ int header_done;
+ int child_pipe[2];
+ int pid;
+ EXTRACT_LIST *elist;
+} ctl_data_t;
+
+/*
+ * Interpose something between the process writing out the dump (writing it to
+ * 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.
+ */
+int
+writer_intermediary(
+ EXTRACT_LIST * elist)
+{
+ 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;
+
+ security_stream_read(amidxtaped_streams[DATAFD].fd,
+ read_amidxtaped_data, &ctl_data);
+
+ while(get_amidxtaped_line() >= 0) {
+ char desired_tape[MAX_TAPE_LABEL_BUF];
+
+ /* 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;
+ }
+ } 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;
+ }
+ }
+
+ /* CTL might be close before DATA */
+ event_loop(0);
+ if (ctl_data.child_pipe[1] != -1)
+ aclose(ctl_data.child_pipe[1]);
+
+ 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"));
+ }
+
+ 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;
+ }
+ }
+ 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 * cwd;
char *l;
- int tape_server_socket;
int first;
int otc;
+ tapelist_t *tlist = NULL, *a_tlist;
+ g_option_t g_options;
+ GSList *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);
l = reply_line();
if (!server_happy())
{
- printf("%s\n", l);
+ g_printf("%s\n", l);
exit(1);
}
/* skip reply number */
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(" ");
- printf(" %s\n", elist->tape);
+ 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(" ");
- printf(" %s\n", elist->tape);
+ 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);
}
- printf("\n");
- getcwd(buf, sizeof(buf));
- printf("Restoring files into directory %s\n", buf);
-#ifdef SAMBA_CLIENT
+ }
+ g_printf("\n");
+
+ cwd = g_get_current_dir();
+ if (cwd == NULL) {
+ perror(_("extract_list: Current working directory unavailable"));
+ exit(1);
+ }
+
+ g_printf(_("Restoring files into directory %s\n"), cwd);
+ check_file_overwrite(cwd);
+
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))
+ g_printf(_("(unless it is a Samba backup, that will go through to the SMB server)\n"));
+ dbprintf(_("Checking with user before restoring into directory %s\n"), cwd);
+ if (!okay_to_continue(0,0,0)) {
+ amfree(cwd);
return;
- printf("\n");
+ }
+ g_printf("\n");
+ if (!do_unlink_list()) {
+ g_fprintf(stderr, _("Can't recover because I can't cleanup the cwd (%s)\n"),
+ cwd);
+ amfree(cwd);
+ return;
+ }
+ free_unlink_list();
+
+ g_options.config = get_config_name();
+ g_options.hostname = dump_hostname;
+ for (elist = first_tape_list(); elist != NULL;
+ elist = next_tape_list(elist)) {
+ all_level = g_slist_append(all_level, GINT_TO_POINTER(elist->level));
+ }
+ if (dump_dle) {
+ g_slist_free(dump_dle->level);
+ dump_dle->level = all_level;
+ run_client_scripts(EXECUTE_ON_PRE_RECOVER, &g_options, dump_dle,
+ stderr);
+ dump_dle->level = 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 %s\n",dump_device_name);
+ 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);
- printf("Load tape %s now\n", elist->tape);
+ 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;
}
dump_datestamp = newstralloc(dump_datestamp, elist->date);
+ if (last_level != -1 && dump_dle) {
+ dump_dle->level = g_slist_append(dump_dle->level,
+ GINT_TO_POINTER(last_level));
+ dump_dle->level = g_slist_append(dump_dle->level,
+ GINT_TO_POINTER(elist->level));
+ run_client_scripts(EXECUTE_ON_INTER_LEVEL_RECOVER, &g_options,
+ dump_dle, stderr);
+ g_slist_free(dump_dle->level);
+ dump_dle->level = NULL;
+ }
+
/* connect to the tape handler daemon on the tape drive server */
- if ((tape_server_socket = 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;
}
-
- /* 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 */
- extract_files_child(tape_server_socket, elist);
- /*NOT REACHED*/
+ if (dump_dle) {
+ dump_dle->level = g_slist_append(dump_dle->level,
+ GINT_TO_POINTER(elist->level));
+ run_client_scripts(EXECUTE_ON_PRE_LEVEL_RECOVER, &g_options,
+ dump_dle, stderr);
}
- /* this is the parent */
- if (pid == -1)
- {
- perror("extract_list - error forking child");
- exit(1);
+ last_level = elist->level;
+
+ /* 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 */
+
+ stop_amidxtaped();
+
+ if (dump_dle) {
+ run_client_scripts(EXECUTE_ON_POST_LEVEL_RECOVER, &g_options,
+ dump_dle, stderr);
+ g_slist_free(dump_dle->level);
+ dump_dle->level = NULL;
}
+ }
+ if (dump_dle) {
+ dump_dle->level = all_level;
+ run_client_scripts(EXECUTE_ON_POST_RECOVER, &g_options, dump_dle,
+ stderr);
+ g_slist_free(dump_dle->level);
+ all_level = NULL;
+ dump_dle->level = NULL;
+ }
+}
- /* store the child pid globally so that it can be killed on intr */
- extract_restore_child_pid = pid;
+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);
- aclose(tape_server_socket);
+ if (pkt->type == P_NAK) {
+#if defined(PACKET_DEBUG)
+ g_fprintf(stderr, _("got nak response:\n----\n%s\n----\n\n"), pkt->body);
+#endif
- /* 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);
+ 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;
}
- if (pid == extract_restore_child_pid)
- {
- extract_restore_child_pid = -1;
+ return;
+ }
+
+ if (pkt->type != P_REP) {
+ errstr = newvstrallocf(errstr, _("received strange packet type %s: %s"),
+ pkt_type2str(pkt->type), pkt->body);
+ *response_error = 1;
+ return;
+ }
+
+#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;
}
- else
- {
- fprintf(stderr, "extract list - unknown child terminated?\n");
- exit(1);
+
+
+ /*
+ * 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 ((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 */
+
+ /*
+ * 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;
}
- else { /* RETRY_TAPE */
+/*
+ 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 {
- delete_tape_list(elist); /* tape done so delete from list */
+/*
+ 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;
}
}
+ /*
+ * 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")) {
+ 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;
+ }
+
+ 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) {
+ ctl_data->header_done = 1;
+ 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->child_pipe[0], ctl_data->elist);
+ /*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]);
+ }
+ /*
+ * 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);
+}
+
+char *
+amidxtaped_client_get_security_conf(
+ char * string,
+ void * arg)
+{
+ (void)arg; /* Quiet unused parameter warning */
+
+ if(!string || !*string)
+ return(NULL);
+
+ if(strcmp(string, "auth")==0) {
+ return(getconf_str(CNF_AUTH));
+ }
+ if(strcmp(string, "ssh_keys")==0) {
+ return(getconf_str(CNF_SSH_KEYS));
+ }
+ return(NULL);
}