* Authors: the Amanda Development Team. Its members are listed in a
* file named AUTHORS, in the root directory of this distribution.
*/
-/* $Id: amidxtaped.c,v 1.25.2.3.4.1.2.11.2.2 2005/10/02 13:48:42 martinea Exp $
+/* $Id: amidxtaped.c,v 1.58 2006/03/14 13:12:01 martinea Exp $
*
* This daemon extracts a dump image off a tape for amrecover and
* returns it over the network. It basically, reads a number of
*/
#include "amanda.h"
-#include "clock.h"
#include "version.h"
+#include "clock.h"
+#include "restore.h"
#include "changer.h"
#include "tapeio.h"
#include "conffile.h"
#include "logfile.h"
+#include "amfeatures.h"
+#include "stream.h"
+
+#define TIMEOUT 30
static char *pgm = "amidxtaped"; /* in case argv[0] is not set */
-char *conf_logdir = NULL;
-char *conf_logfile = NULL;
-int get_lock = 0;
-char *found_device = NULL;
+extern char *rst_conf_logdir;
+extern char *rst_conf_logfile;
+extern char *config_dir;
+
+static int get_lock = 0;
+
+static am_feature_t *our_features = NULL;
+static am_feature_t *their_features = NULL;
static char *get_client_line P((void));
+static char *get_client_line_fd P((int));
+static void check_security_buffer P((char*));
+
+/* exit routine */
+static int parent_pid = -1;
+static void cleanup P((void));
/* get a line from client - line terminated by \r\n */
static char *
}
amfree(line);
amfree(part);
- if(get_lock) {
- unlink(conf_logfile);
- }
dbclose();
exit(1);
/* NOTREACHED */
return line;
}
-int found = 0;
-char *searchlabel = NULL;
-int nslots, backwards;
-
-int scan_init(rc, ns, bk)
-int rc, ns, bk;
-{
- if(rc) {
- if(get_lock) {
- unlink(conf_logfile);
- }
- error("could not get changer info: %s", changer_resultstr);
- }
- nslots = ns;
- backwards = bk;
-
- return 0;
-}
-
-
-int taperscan_slot(rc, slotstr, device)
-int rc;
-char *slotstr;
-char *device;
+/* get a line from client - line terminated by \r\n */
+static char *
+get_client_line_fd(fd)
+int fd;
{
- char *errstr;
- char *datestamp = NULL, *label = NULL;
+ static char *line = NULL;
+ static int line_size = 0;
+ char *s = line;
+ int len = 0;
+ char c;
+ int nb;
- if(rc == 2) {
- dbprintf(("%s: fatal slot %s: %s\n", debug_prefix_time(NULL),
- slotstr, changer_resultstr));
- return 1;
- }
- else if(rc == 1) {
- dbprintf(("%s: slot %s: %s\n", debug_prefix_time(NULL),
- slotstr, changer_resultstr));
- return 0;
+ if(line == NULL) { /* first time only, allocate initial buffer */
+ s = line = malloc(128);
+ line_size = 128;
}
- else {
- if((errstr = tape_rdlabel(device, &datestamp, &label)) != NULL) {
- dbprintf(("%s: slot %s: %s\n", debug_prefix_time(NULL),
- slotstr, errstr));
- } else {
- /* got an amanda tape */
- dbprintf(("%s: slot %s: date %-8s label %s",
- debug_prefix_time(NULL), slotstr, datestamp, label));
- if(strcmp(label, FAKE_LABEL) == 0 ||
- strcmp(label, searchlabel) == 0) {
- /* it's the one we are looking for, stop here */
- found_device = newstralloc(found_device,device);
- dbprintf((" (exact label match)\n"));
- found = 1;
- return 1;
- }
- else {
- dbprintf((" (no match)\n"));
- }
- }
+ while(1) {
+ nb = read(fd, &c, 1);
+ if (nb <= 0) {
+ /* EOF or error */
+ if ((nb <= 0) && ((errno == EINTR) || (errno == EAGAIN))) {
+ /* Keep looping if failure is temporary */
+ continue;
+ }
+ dbprintf(("%s: Control pipe read error - %s\n",
+ pgm, strerror(errno)));
+ break;
+ }
+
+ if(len >= line_size-1) { /* increase buffer size */
+ line_size *= 2;
+ line = realloc(line, line_size);
+ if (line == NULL) {
+ error("Memory reallocation failure");
+ /* NOTREACHED */
+ }
+ s = &line[len];
+ }
+ *s = c;
+ if(c == '\n') {
+ if(len > 0 && *(s-1) == '\r') { /* remove '\r' */
+ s--;
+ len--;
+ }
+ *s = '\0';
+ return line;
+ }
+ s++;
+ len++;
}
- return 0;
+ line[len] = '\0';
+ return line;
}
-int lock_logfile()
+void check_security_buffer(buffer)
+ char *buffer;
{
- conf_logdir = getconf_str(CNF_LOGDIR);
- if (*conf_logdir == '/') {
- conf_logdir = stralloc(conf_logdir);
- } else {
- conf_logdir = stralloc2(config_dir, conf_logdir);
- }
- conf_logfile = vstralloc(conf_logdir, "/log", NULL);
- if (access(conf_logfile, F_OK) == 0) {
- error("%s exists: amdump or amflush is already running, or you must run amcleanup", conf_logfile);
- }
- log_add(L_INFO, "amidxtaped");
- return 1;
+ socklen_t i;
+ struct sockaddr_in addr;
+ char *s, *fp, ch;
+ char *errstr = NULL;
+
+ i = sizeof (addr);
+ if (getpeername(0, (struct sockaddr *)&addr, &i) == -1)
+ error("getpeername: %s", strerror(errno));
+ if (addr.sin_family != AF_INET || ntohs(addr.sin_port) == 20) {
+ error("connection rejected from %s family %d port %d",
+ inet_ntoa(addr.sin_addr), addr.sin_family, htons(addr.sin_port));
+ }
+
+ /* do the security thing */
+ s = buffer;
+ ch = *s++;
+
+ skip_whitespace(s, ch);
+ if (ch == '\0') {
+ error("cannot parse SECURITY line");
+ }
+ fp = s-1;
+ skip_non_whitespace(s, ch);
+ s[-1] = '\0';
+ if (strcmp(fp, "SECURITY") != 0) {
+ error("cannot parse SECURITY line");
+ }
+ skip_whitespace(s, ch);
+ if (!check_security(&addr, s-1, 0, &errstr)) {
+ error("security check failed: %s", errstr);
+ }
}
-
int main(argc, argv)
int argc;
char **argv;
{
- int amrestore_nargs;
- char **amrestore_args;
char *buf = NULL;
- int i;
+ int data_sock = -1, data_port = -1;
socklen_t socklen;
- char *amrestore_path;
- pid_t pid;
- int isafile;
- struct stat stat_tape;
- char *tapename = NULL;
- char *s, *fp;
- int ch;
- char *errstr = NULL;
struct sockaddr_in addr;
- amwait_t status;
-
- int re_header = 0;
+ match_list_t *match_list;
+ tapelist_t *tapes = NULL;
+ char *their_feature_string = NULL;
+ rst_flags_t *rst_flags;
+ int use_changer = 0;
+ FILE *prompt_stream = NULL;
int re_end = 0;
- char *re_label = NULL;
- char *re_fsf = NULL;
- char *re_device = NULL;
- char *re_host = NULL;
- char *re_disk = NULL;
- char *re_datestamp = NULL;
char *re_config = NULL;
+ char *conf_tapetype;
+ tapetype_t *tape;
safe_fd(-1, 0);
safe_cd();
+ /* Don't die when child closes pipe */
+ signal(SIGPIPE, SIG_IGN);
+
+ rst_flags = new_rst_flags();
+ rst_flags->mask_splits = 1; /* for older clients */
+ rst_flags->amidxtaped = 1;
+ our_features = am_init_feature_set();
+ their_features = am_set_default_feature_set();
+
/*
* When called via inetd, it is not uncommon to forget to put the
* argv[0] value on the config line. On some systems (e.g. Solaris)
/* do the security thing */
amfree(buf);
buf = stralloc(get_client_line());
- s = buf;
- ch = *s++;
-
- skip_whitespace(s, ch);
- if (ch == '\0')
- {
- error("cannot parse SECURITY line");
- }
- fp = s-1;
- skip_non_whitespace(s, ch);
- s[-1] = '\0';
- if (strcmp(fp, "SECURITY") != 0)
- {
- error("cannot parse SECURITY line");
- }
- skip_whitespace(s, ch);
- if (!security_ok(&addr, s-1, 0, &errstr)) {
- error("security check failed: %s", errstr);
- }
+ check_security_buffer(buf);
/* get the number of arguments */
- amrestore_nargs = 0;
- do {
+ match_list = alloc(sizeof(match_list_t));
+ match_list->next = NULL;
+ match_list->hostname = "";
+ match_list->datestamp = "";
+ match_list->level = "";
+ match_list->diskname = "";
+
+ do {
amfree(buf);
buf = stralloc(get_client_line());
if(strncmp(buf, "LABEL=", 6) == 0) {
- re_label = stralloc(buf+6);
+ tapes = unmarshal_tapelist_str(buf+6);
}
else if(strncmp(buf, "FSF=", 4) == 0) {
- int fsf = atoi(buf+4);
- if(fsf > 0) {
- re_fsf = stralloc(buf+4);
- }
+ rst_flags->fsf = atoi(buf + 4);
}
else if(strncmp(buf, "HEADER", 6) == 0) {
- re_header = 1;
+ rst_flags->headers = 1;
+ }
+ else if(strncmp(buf, "FEATURES=", 9) == 0) {
+ char *our_feature_string = NULL;
+ their_feature_string = stralloc(buf+9);
+ am_release_feature_set(their_features);
+ their_features = am_string_to_feature(their_feature_string);
+ amfree(their_feature_string);
+ our_feature_string = am_feature_to_string(our_features);
+ printf("%s", our_feature_string);
+ fflush(stdout);
+ amfree(our_feature_string);
}
else if(strncmp(buf, "DEVICE=", 7) == 0) {
- re_device = stralloc(buf+7);
+ rst_flags->alt_tapedev= stralloc(buf+7);
}
else if(strncmp(buf, "HOST=", 5) == 0) {
- re_host = stralloc(buf+5);
+ match_list->hostname = stralloc(buf+5);
}
else if(strncmp(buf, "DISK=", 5) == 0) {
- re_disk = stralloc(buf+5);
+ match_list->diskname = stralloc(buf+5);
}
else if(strncmp(buf, "DATESTAMP=", 10) == 0) {
- re_datestamp = stralloc(buf+10);
+ match_list->datestamp = stralloc(buf+10);
}
else if(strncmp(buf, "END", 3) == 0) {
re_end = 1;
re_config = stralloc(buf+7);
}
else if(buf[0] != '\0' && buf[0] >= '0' && buf[0] <= '9') {
- amrestore_nargs = atoi(buf);
+/* XXX does nothing? amrestore_nargs = atoi(buf); */
re_end = 1;
}
else {
}
} while (re_end == 0);
+ amfree(buf);
+
+ if(!tapes && rst_flags->alt_tapedev){
+ dbprintf(("%s: Looks like we're restoring from a holding file...\n", debug_prefix_time(NULL)));
+ tapes = unmarshal_tapelist_str(rst_flags->alt_tapedev);
+ tapes->isafile = 1;
+ amfree(rst_flags->alt_tapedev);
+ rst_flags->alt_tapedev = NULL;
+ }
if(re_config) {
char *conffile;
if (read_conffile(conffile)) {
dbprintf(("%s: config '%s' not found\n",
debug_prefix_time(NULL), re_config));
- amfree(re_fsf);
- amfree(re_label);
amfree(re_config);
+ re_config = NULL;
}
- }
- else {
- amfree(re_fsf);
- amfree(re_label);
- }
-
- if(re_device && re_config &&
- strcmp(re_device, getconf_str(CNF_TAPEDEV)) == 0) {
+ amfree(conffile);
+ }
+
+ if(tapes &&
+ (!rst_flags->alt_tapedev ||
+ (re_config && ( strcmp(rst_flags->alt_tapedev,
+ getconf_str(CNF_AMRECOVER_CHANGER)) == 0 ||
+ strcmp(rst_flags->alt_tapedev,
+ getconf_str(CNF_TPCHANGER)) == 0 ) ) ) ) {
+ /* We need certain options, if restoring from more than one tape */
+ if(tapes->next && !am_has_feature(their_features, fe_recover_splits)) {
+ error("%s: Client must support split dumps to restore requested data.", get_pname());
+ /* NOTREACHED */
+ }
+ dbprintf(("%s: Restoring from changer, checking labels\n", get_pname()));
+ rst_flags->check_labels = 1;
+ use_changer = 1;
+ }
+
+ /* If we'll be stepping on the tape server's devices, lock them. */
+ if(re_config &&
+ (use_changer || (rst_flags->alt_tapedev &&
+ strcmp(rst_flags->alt_tapedev,
+ getconf_str(CNF_TAPEDEV)) == 0) ) ) {
+ dbprintf(("%s: Locking devices\n", get_pname()));
+ parent_pid = getpid();
+ atexit(cleanup);
get_lock = lock_logfile();
}
- if(re_label && re_config &&
- strcmp(re_device, getconf_str(CNF_AMRECOVER_CHANGER)) == 0) {
-
- if(changer_init() == 0) {
- dbprintf(("%s: No changer available\n",
- debug_prefix_time(NULL)));
- }
- else {
- searchlabel = stralloc(re_label);
- changer_find(scan_init, taperscan_slot, searchlabel);
- if(found == 0) {
- dbprintf(("%s: Can't find label \"%s\"\n",
- debug_prefix_time(NULL), searchlabel));
- if(get_lock) {
- unlink(conf_logfile);
- }
- dbclose();
- exit(1);
- }
- else {
- re_device=stralloc(found_device);
- dbprintf(("%s: label \"%s\" found\n",
- debug_prefix_time(NULL), searchlabel));
- }
- }
+ /* Init the tape changer */
+ if(tapes && use_changer && changer_init() == 0) {
+ dbprintf(("%s: No changer available\n", debug_prefix_time(NULL)));
}
- if(re_fsf && re_config && getconf_int(CNF_AMRECOVER_DO_FSF) == 0) {
- amfree(re_fsf);
- }
- if(re_label && re_config && getconf_int(CNF_AMRECOVER_CHECK_LABEL) == 0) {
- amfree(re_label);
+ /* Read the default block size from the tape type */
+ if(re_config && (conf_tapetype = getconf_str(CNF_TAPETYPE)) != NULL) {
+ tape = lookup_tapetype(conf_tapetype);
+ rst_flags->blocksize = tape->blocksize * 1024;
}
- dbprintf(("%s: amrestore_nargs=%d\n",
- debug_prefix_time(NULL),
- amrestore_nargs));
-
- amrestore_args = (char **)alloc((amrestore_nargs+12)*sizeof(char *));
- i = 0;
- amrestore_args[i++] = "amrestore";
- if(re_header || re_device || re_host || re_disk || re_datestamp ||
- re_label || re_fsf) {
-
- amrestore_args[i++] = "-p";
- if(re_header) amrestore_args[i++] = "-h";
- if(re_label) {
- amrestore_args[i++] = "-l";
- amrestore_args[i++] = re_label;
- }
- if(re_fsf) {
- amrestore_args[i++] = "-f";
- amrestore_args[i++] = re_fsf;
- }
- if(re_device) amrestore_args[i++] = re_device;
- if(re_host) amrestore_args[i++] = re_host;
- if(re_disk) amrestore_args[i++] = re_disk;
- if(re_datestamp) amrestore_args[i++] = re_datestamp;
+ if(rst_flags->fsf && re_config &&
+ getconf_int(CNF_AMRECOVER_DO_FSF) == 0) {
+ rst_flags->fsf = 0;
}
- else { /* fe_amidxtaped_nargs */
- while (i <= amrestore_nargs) {
- amrestore_args[i++] = stralloc(get_client_line());
- }
- }
- amrestore_args[i] = NULL;
-
- amrestore_path = vstralloc(sbindir, "/", "amrestore", NULL);
- /* so got all the arguments, now ready to execv */
- dbprintf(("%s: Ready to execv amrestore with:\n", debug_prefix_time(NULL)));
- dbprintf(("path = %s\n", amrestore_path));
- for (i = 0; amrestore_args[i] != NULL; i++)
- {
- dbprintf(("argv[%d] = \"%s\"\n", i, amrestore_args[i]));
- }
-
- if ((pid = fork()) == 0)
- {
- /* child */
- (void)execv(amrestore_path, amrestore_args);
-
- /* only get here if exec failed */
- dbprintf(("%s: child could not exec %s: %s\n",
- debug_prefix_time(NULL),
- amrestore_path,
- strerror(errno)));
- return 1;
- /*NOT REACHED*/
+ if(re_config && getconf_int(CNF_AMRECOVER_CHECK_LABEL) == 0) {
+ rst_flags->check_labels = 0;
}
- /* this is the parent */
- if (pid == -1)
- {
- dbprintf(("%s: error forking amrestore child: %s\n",
- debug_prefix_time(NULL), strerror(errno)));
- if(get_lock) {
- unlink(conf_logfile);
+ /* establish a distinct data connection for dumpfile data */
+ if(am_has_feature(their_features, fe_recover_splits)){
+ int data_fd;
+ char *buf;
+
+ dbprintf(("%s: Client understands split dumpfiles\n", get_pname()));
+
+ if((data_sock = stream_server(&data_port, STREAM_BUFSIZE, -1)) < 0){
+ error("%s: could not create data socket: %s", get_pname(),
+ strerror(errno));
}
- dbclose();
- return 1;
- }
-
- /* wait for the child to do the restore */
- if (waitpid(pid, &status, 0) == -1)
- {
- dbprintf(("%s: error waiting for amrestore child: %s\n",
- debug_prefix_time(NULL), strerror(errno)));
- if(get_lock) {
- unlink(conf_logfile);
+ dbprintf(("%s: Local port %d set aside for data\n", get_pname(),
+ data_port));
+
+ printf("CONNECT %d\n", data_port); /* tell client where to connect */
+ fflush(stdout);
+
+ if((data_fd = stream_accept(data_sock, TIMEOUT, -1, -1)) < 0){
+ error("stream_accept failed for client data connection: %s\n",
+ strerror(errno));
}
- dbclose();
- return 1;
- }
- /* amrestore often sees the pipe reader (ie restore) quit in the middle
- of the file because it has extracted all of the files needed. This
- results in an exit status of 2. This unfortunately is the exit
- status returned by many errors. Only when the exit status is 1 is it
- guaranteed that an error existed. In all cases we should rewind the
- tape if we can so that a retry starts from the correct place */
- if (WIFEXITED(status) != 0)
- {
- dbprintf(("%s: amrestore terminated normally with status: %d\n",
- debug_prefix_time(NULL), WEXITSTATUS(status)));
- }
- else
- {
- dbprintf(("%s: amrestore terminated abnormally.\n",
- debug_prefix_time(NULL)));
- }
+ buf = get_client_line_fd(data_fd);
- /* rewind tape */
- if(re_device) {
- tapename = re_device;
+ check_security_buffer(buf);
+ rst_flags->pipe_to_fd = data_fd;
+ prompt_stream = stdout;
}
else {
- /* the first non-option argument is the tape device */
- for (i = 1; i <= amrestore_nargs; i++)
- if (amrestore_args[i][0] != '-')
- break;
- if (i > amrestore_nargs) {
- dbprintf(("%s: Couldn't find tape in arguments\n",
- debug_prefix_time(NULL)));
- if(get_lock) {
- unlink(conf_logfile);
- }
- dbclose();
- return 1;
- }
-
- tapename = stralloc(amrestore_args[i]);
- }
- if (tape_stat(tapename, &stat_tape) != 0) {
- error("could not stat %s: %s", tapename, strerror(errno));
- }
- isafile = S_ISREG((stat_tape.st_mode));
- if (!isafile) {
- char *errstr = NULL;
-
- dbprintf(("%s: rewinding tape ...\n", debug_prefix_time(NULL)));
- errstr = tape_rewind(tapename);
-
- if (errstr != NULL) {
- dbprintf(("%s: %s\n", debug_prefix_time(NULL), errstr));
- amfree(errstr);
- } else {
- dbprintf(("%s: done\n", debug_prefix_time(NULL)));
- }
- }
-
- if(get_lock) {
- unlink(conf_logfile);
- }
-
- amfree(tapename);
+ rst_flags->pipe_to_fd = fileno(stdout);
+ prompt_stream = stderr;
+ }
+ dbprintf(("%s: Sending output to file descriptor %d\n",
+ get_pname(), rst_flags->pipe_to_fd));
+
+
+ /* make sure our restore flags aren't crazy */
+ if(check_rst_flags(rst_flags) == -1){
+ if(rst_flags->pipe_to_fd != -1) aclose(rst_flags->pipe_to_fd);
+ exit(1);
+ }
+
+ /* actual restoration */
+ search_tapes(prompt_stream, use_changer, tapes, match_list, rst_flags,
+ their_features);
+ dbprintf(("%s: Restoration finished\n", debug_prefix_time(NULL)));
+
+ /* cleanup */
+ if(rst_flags->pipe_to_fd != -1) aclose(rst_flags->pipe_to_fd);
+ free_tapelist(tapes);
+
+ am_release_feature_set(their_features);
+
+ amfree(rst_flags->alt_tapedev);
+ amfree(rst_flags);
+ amfree(match_list->hostname);
+ amfree(match_list->diskname);
+ amfree(match_list->datestamp);
+ amfree(match_list);
+ amfree(config_dir);
+ amfree(re_config);
dbclose();
return 0;
}
+
+static void
+cleanup(void)
+{
+ if(parent_pid == getpid()) {
+ if(get_lock) unlink(rst_conf_logfile);
+ }
+}
+