* 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.58 2006/03/14 13:12:01 martinea Exp $
+/* $Id: amidxtaped.c,v 1.73 2006/07/25 19:06:46 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 "logfile.h"
#include "amfeatures.h"
#include "stream.h"
+#include "amandad.h"
#define TIMEOUT 30
static char *pgm = "amidxtaped"; /* in case argv[0] is not set */
-extern char *rst_conf_logdir;
extern char *rst_conf_logfile;
extern char *config_dir;
static int get_lock = 0;
+static int from_amandad;
static am_feature_t *our_features = NULL;
static am_feature_t *their_features = NULL;
+static g_option_t *g_options = NULL;
+static int ctlfdin, ctlfdout, datafdout;
+static char *amandad_auth = NULL;
-static char *get_client_line P((void));
-static char *get_client_line_fd P((int));
-static void check_security_buffer P((char*));
+static char *get_client_line(void);
+static void check_security_buffer(char *);
+static char *get_client_line_fd(int);
/* exit routine */
-static int parent_pid = -1;
-static void cleanup P((void));
+static pid_t parent_pid = -1;
+static void cleanup(void);
+
+int main(int argc, char **argv);
/* get a line from client - line terminated by \r\n */
static char *
-get_client_line()
+get_client_line(void)
{
static char *line = NULL;
char *part = NULL;
- int len;
+ size_t len;
amfree(line);
while(1) {
amfree(part);
dbclose();
exit(1);
- /* NOTREACHED */
+ /*NOTREACHED*/
}
if(line) {
strappend(line, part);
/* get a line from client - line terminated by \r\n */
static char *
-get_client_line_fd(fd)
-int fd;
+get_client_line_fd(
+ int fd)
{
static char *line = NULL;
- static int line_size = 0;
+ static size_t line_size = 0;
char *s = line;
- int len = 0;
+ size_t len = 0;
char c;
- int nb;
+ ssize_t nb;
if(line == NULL) { /* first time only, allocate initial buffer */
- s = line = malloc(128);
+ s = line = alloc(128);
line_size = 128;
}
while(1) {
continue;
}
dbprintf(("%s: Control pipe read error - %s\n",
- pgm, strerror(errno)));
+ pgm, strerror(errno)));
break;
}
line = realloc(line, line_size);
if (line == NULL) {
error("Memory reallocation failure");
- /* NOTREACHED */
+ /*NOTREACHED*/
}
s = &line[len];
}
}
-void check_security_buffer(buffer)
- char *buffer;
+void
+check_security_buffer(
+ char * buffer)
{
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",
+
+ dbprintf(("%s: check_security_buffer(buffer='%s')\n",
+ debug_prefix(NULL), buffer));
+
+ i = SIZEOF(addr);
+ if (getpeername(0, (struct sockaddr *)&addr, &i) == -1) {
+ error("getpeername: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+ if ((addr.sin_family != (sa_family_t)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);
- }
+ /*NOTREACHED*/
+ }
+
+ /* do the security thing */
+ s = buffer;
+ ch = *s++;
+
+ skip_whitespace(s, ch);
+ if (ch == '\0') {
+ error("cannot parse SECURITY line");
+ /*NOTREACHED*/
+ }
+ fp = s-1;
+ skip_non_whitespace(s, ch);
+ s[-1] = '\0';
+ if (strcmp(fp, "SECURITY") != 0) {
+ error("cannot parse SECURITY line");
+ /*NOTREACHED*/
+ }
+ skip_whitespace(s, ch);
+ if (!check_security(&addr, s-1, 0, &errstr)) {
+ error("security check failed: %s", errstr);
+ /*NOTREACHED*/
+ }
}
-int main(argc, argv)
-int argc;
-char **argv;
+int
+main(
+ int argc,
+ char ** argv)
{
char *buf = NULL;
- int data_sock = -1, data_port = -1;
+ int data_sock = -1;
+ in_port_t data_port = (in_port_t)-1;
socklen_t socklen;
struct sockaddr_in addr;
match_list_t *match_list;
rst_flags_t *rst_flags;
int use_changer = 0;
FILE *prompt_stream = NULL;
- int re_end = 0;
+ int re_end;
char *re_config = NULL;
char *conf_tapetype;
tapetype_t *tape;
+ char *line;
- safe_fd(-1, 0);
+ safe_fd(DATA_FD_OFFSET, 4);
safe_cd();
/* Don't die when child closes pipe */
set_pname(pgm);
+ if(argv && argv[1] && strcmp(argv[1], "amandad") == 0) {
+ from_amandad = 1;
+ if(argv[2])
+ amandad_auth = argv[2];
+ }
+ else {
+ from_amandad = 0;
+ safe_fd(-1, 0);
+ }
+
#ifdef FORCE_USERID
/* we'd rather not run as root */
if(geteuid() == 0) {
if(client_uid == (uid_t) -1) {
error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
+ /*NOTREACHED*/
}
+ /*@ignore@*/
initgroups(CLIENT_LOGIN, client_gid);
+ /*@end@*/
setgid(client_gid);
setuid(client_uid);
}
chats to stderr, which we don't want going to client */
/* if no debug file, ship to bit bucket */
(void)close(STDERR_FILENO);
- dbopen();
+ dbopen(DBG_SUBDIR_SERVER);
startclock();
dbprintf(("%s: version %s\n", pgm, version()));
#ifdef DEBUG_CODE
debug_prefix_time(NULL)));
}
- socklen = sizeof (addr);
- if (getpeername(0, (struct sockaddr *)&addr, &socklen) == -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));
- }
+ if(from_amandad == 0) {
+ socklen = SIZEOF(addr);
+ if (getpeername(0, (struct sockaddr *)&addr, &socklen) == -1) {
+ error("getpeername: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+ if ((addr.sin_family != (sa_family_t)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));
+ /*NOTREACHED*/
+ }
- /* do the security thing */
- amfree(buf);
- buf = stralloc(get_client_line());
- check_security_buffer(buf);
+ /* do the security thing */
+ amfree(buf);
+ buf = stralloc(get_client_line());
+ check_security_buffer(buf);
+ }
+ else {
+ ctlfdout = DATA_FD_OFFSET + 0;
+ ctlfdin = DATA_FD_OFFSET + 1;
+ datafdout = DATA_FD_OFFSET + 2;
+ close(DATA_FD_OFFSET +3);
+
+ /* read the REQ packet */
+ for(; (line = agets(stdin)) != NULL; free(line)) {
+#define sc "OPTIONS "
+ if(strncmp(line, sc, sizeof(sc)-1) == 0) {
+#undef sc
+ g_options = parse_g_options(line+8, 1);
+ if(!g_options->hostname) {
+ g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
+ gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
+ g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
+ }
+ }
+ }
+ amfree(line);
+
+ if(amandad_auth && g_options->auth) {
+ if(strcasecmp(amandad_auth, g_options->auth) != 0) {
+ printf("ERROR recover program ask for auth=%s while amidxtaped is configured for '%s'\n",
+ g_options->auth, amandad_auth);
+ error("ERROR recover program ask for auth=%s while amidxtaped is configured for '%s'",
+ g_options->auth, amandad_auth);
+ /*NOTREACHED*/
+ }
+ }
+ /* send the REP packet */
+ printf("CONNECT CTL %d DATA %d\n", DATA_FD_OFFSET, DATA_FD_OFFSET+1);
+ printf("\n");
+ fflush(stdout);
+ fflush(stdin);
+ if ((dup2(ctlfdout, fileno(stdout)) < 0)
+ || (dup2(ctlfdin, fileno(stdin)) < 0)) {
+ error("amandad: Failed to setup stdin or stdout");
+ /*NOTREACHED*/
+ }
+ }
/* get the number of arguments */
- match_list = alloc(sizeof(match_list_t));
+ match_list = alloc(SIZEOF(match_list_t));
match_list->next = NULL;
match_list->hostname = "";
match_list->datestamp = "";
match_list->level = "";
match_list->diskname = "";
- do {
+ for (re_end = 0; re_end == 0; ) {
amfree(buf);
buf = stralloc(get_client_line());
if(strncmp(buf, "LABEL=", 6) == 0) {
tapes = unmarshal_tapelist_str(buf+6);
}
else if(strncmp(buf, "FSF=", 4) == 0) {
- rst_flags->fsf = atoi(buf + 4);
+ rst_flags->fsf = OFF_T_ATOI(buf + 4);
}
else if(strncmp(buf, "HEADER", 6) == 0) {
rst_flags->headers = 1;
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);
+ if(from_amandad == 1)
+ printf("FEATURES=%s\r\n", our_feature_string);
+ else
+ printf("%s", our_feature_string);
fflush(stdout);
amfree(our_feature_string);
}
/* XXX does nothing? amrestore_nargs = atoi(buf); */
re_end = 1;
}
- else {
- }
- } while (re_end == 0);
+ }
amfree(buf);
if(!tapes && rst_flags->alt_tapedev){
re_config = NULL;
}
amfree(conffile);
+
+ dbrename(config_name, DBG_SUBDIR_SERVER);
}
- if(tapes &&
+ if(tapes &&
(!rst_flags->alt_tapedev ||
(re_config && ( strcmp(rst_flags->alt_tapedev,
getconf_str(CNF_AMRECOVER_CHANGER)) == 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 */
+ /*NOTREACHED*/
}
dbprintf(("%s: Restoring from changer, checking labels\n", get_pname()));
rst_flags->check_labels = 1;
}
/* If we'll be stepping on the tape server's devices, lock them. */
- if(re_config &&
+ if(re_config &&
(use_changer || (rst_flags->alt_tapedev &&
strcmp(rst_flags->alt_tapedev,
getconf_str(CNF_TAPEDEV)) == 0) ) ) {
/* 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;
+ rst_flags->blocksize = tapetype_get_blocksize(tape) * 1024;
}
- if(rst_flags->fsf && re_config &&
- getconf_int(CNF_AMRECOVER_DO_FSF) == 0) {
- rst_flags->fsf = 0;
+ if(rst_flags->fsf && re_config &&
+ getconf_boolean(CNF_AMRECOVER_DO_FSF) == 0) {
+ rst_flags->fsf = (off_t)0;
}
- if(re_config && getconf_int(CNF_AMRECOVER_CHECK_LABEL) == 0) {
+ if (!use_changer && re_config &&
+ getconf_boolean(CNF_AMRECOVER_CHECK_LABEL) == 0) {
rst_flags->check_labels = 0;
}
/* 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));
- }
- 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));
+ if(am_has_feature(their_features, fe_recover_splits)) {
+ if(from_amandad == 1) {
+ rst_flags->pipe_to_fd = datafdout;
+ prompt_stream = stdout;
}
+ else {
+ int data_fd;
+ char *buf;
- buf = get_client_line_fd(data_fd);
+ dbprintf(("%s: Client understands split dumpfiles\n",get_pname()));
- check_security_buffer(buf);
- rst_flags->pipe_to_fd = data_fd;
- prompt_stream = stdout;
+ if((data_sock = stream_server(&data_port, STREAM_BUFSIZE,
+ STREAM_BUFSIZE, 0)) < 0){
+ error("%s: could not create data socket: %s", get_pname(),
+ strerror(errno));
+ /*NOTREACHED*/
+ }
+ dbprintf(("%s: Local port %d set aside for data\n", get_pname(), data_port));
+
+ /* tell client where to connect */
+ printf("CONNECT %hu\n", (unsigned short)data_port);
+ fflush(stdout);
+
+ if((data_fd = stream_accept(data_sock, TIMEOUT, STREAM_BUFSIZE,
+ STREAM_BUFSIZE)) < 0){
+ error("stream_accept failed for client data connection: %s\n",
+ strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ buf = get_client_line_fd(data_fd);
+
+ check_security_buffer(buf);
+ rst_flags->pipe_to_fd = data_fd;
+ prompt_stream = stdout;
+ }
}
else {
rst_flags->pipe_to_fd = fileno(stdout);
}
dbprintf(("%s: Sending output to file descriptor %d\n",
get_pname(), rst_flags->pipe_to_fd));
-
-
+
+
+ if(get_lock == 0 &&
+ re_config &&
+ (use_changer || (rst_flags->alt_tapedev &&
+ strcmp(rst_flags->alt_tapedev,
+ getconf_str(CNF_TAPEDEV)) == 0) ) ) {
+ send_message(prompt_stream, rst_flags, their_features,
+ "%s exists: amdump or amflush is already running, "
+ "or you must run amcleanup",
+ rst_conf_logfile);
+ error("%s exists: amdump or amflush is already running, "
+ "or you must run amcleanup",
+ rst_conf_logfile);
+ }
+
/* 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);
+ if (check_rst_flags(rst_flags) == -1) {
+ if (rst_flags->pipe_to_fd != -1)
+ aclose(rst_flags->pipe_to_fd);
+ send_message(prompt_stream, rst_flags, their_features,
+ "restore flags are crazy");
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);
if(get_lock) unlink(rst_conf_logfile);
}
}
-