Imported Upstream version 2.5.2p1
[debian/amanda] / restore-src / amidxtaped.c
index a3a27f8057a12a1c5c3b87e64d2a58b1882cf014..2e7492c87a096988a9e2c6231b8938cf7646e056 100644 (file)
@@ -23,7 +23,7 @@
  * 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 amidxtaped_debug(i,x) do {     \
+       if ((i) <= debug_amidxtaped) {  \
+           dbprintf(x);                \
+       }                               \
+} while (0)
 
 #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 FILE *cmdin, *cmdout;
 
-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(FILE *in);
+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(FILE *in)
 {
     static char *line = NULL;
     char *part = NULL;
-    int len;
+    size_t len;
 
     amfree(line);
     while(1) {
-       if((part = agets(stdin)) == NULL) {
+       if((part = agets(in)) == NULL) {
            if(errno != 0) {
                dbprintf(("%s: read error: %s\n",
                          debug_prefix_time(NULL), strerror(errno)));
@@ -92,7 +105,7 @@ get_client_line()
            amfree(part);
            dbclose();
            exit(1);
-           /* NOTREACHED */
+           /*NOTREACHED*/
        }
        if(line) {
            strappend(line, part);
@@ -118,18 +131,18 @@ get_client_line()
 
 /* 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) {
@@ -141,7 +154,7 @@ int fd;
                continue;
            }
            dbprintf(("%s: Control pipe read error - %s\n",
-               pgm, strerror(errno)));
+                     pgm, strerror(errno)));
            break;
        }
 
@@ -150,7 +163,7 @@ int fd;
            line = realloc(line, line_size);
            if (line == NULL) {
                error("Memory reallocation failure");
-               /* NOTREACHED */
+               /*NOTREACHED*/
            }
            s = &line[len];
        }
@@ -171,48 +184,61 @@ int fd;
 }
 
 
-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_time(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((struct sockaddr_storage *)&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;
@@ -220,13 +246,17 @@ char **argv;
     char *their_feature_string = NULL;
     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;
+    char *tapedev;
+#ifndef DEBUG_CODE
+    int i;
+#endif
 
-    safe_fd(-1, 0);
+    safe_fd(DATA_FD_OFFSET, 4);
     safe_cd();
 
     /* Don't die when child closes pipe */
@@ -254,6 +284,16 @@ char **argv;
 
     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 */
@@ -261,9 +301,12 @@ char **argv;
     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);
     }
@@ -275,7 +318,7 @@ char **argv;
        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
@@ -304,75 +347,133 @@ char **argv;
                  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);
+       fflush(stdout);
+       cmdout = stdout;
+       cmdin  = stdin;
+       buf = stralloc(get_client_line(cmdin));
+       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)) {
+           if(strncmp_const(line, "OPTIONS ") == 0) {
+               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);
+       fclose(stdin);
+       fclose(stdout);
+       cmdout = fdopen(ctlfdout, "a");
+       if (!cmdout) {
+           error("amidxtaped: Can't fdopen(ctlfdout): %s", strerror(errno));
+           /*NOTREACHED*/
+       }
+       cmdin = fdopen(ctlfdin, "r");
+       if (!cmdin) {
+           error("amidxtaped: Can't fdopen(ctlfdin): %s", strerror(errno));
+           /*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; ) {
+       char *s, ch;
        amfree(buf);
-       buf = stralloc(get_client_line());
-       if(strncmp(buf, "LABEL=", 6) == 0) {
-           tapes = unmarshal_tapelist_str(buf+6);
+       buf = stralloc(get_client_line(cmdin));
+       s = buf;
+       if(strncmp_const_skip(buf, "LABEL=", s, ch) == 0) {
+           tapes = unmarshal_tapelist_str(s);
        }
-       else if(strncmp(buf, "FSF=", 4) == 0) {
-           rst_flags->fsf = atoi(buf + 4);
+       else if(strncmp_const_skip(buf, "FSF=", s, ch) == 0) {
+           rst_flags->fsf = OFF_T_ATOI(s);
        }
-       else if(strncmp(buf, "HEADER", 6) == 0) {
+       else if(strncmp_const_skip(buf, "HEADER", s, ch) == 0) {
            rst_flags->headers = 1;
        }
-       else if(strncmp(buf, "FEATURES=", 9) == 0) {
+       else if(strncmp_const_skip(buf, "FEATURES=", s, ch) == 0) {
            char *our_feature_string = NULL;
-           their_feature_string = stralloc(buf+9);
+           their_feature_string = stralloc(s);
            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);
+           if(from_amandad == 1) 
+               fprintf(cmdout,"FEATURES=%s\r\n", our_feature_string);
+           else
+               fprintf(cmdout,"%s", our_feature_string);
+           fflush(cmdout);
            amfree(our_feature_string);
        }
-       else if(strncmp(buf, "DEVICE=", 7) == 0) {
-           rst_flags->alt_tapedev= stralloc(buf+7);
+       else if(strncmp_const_skip(buf, "DEVICE=", s, ch) == 0) {
+           rst_flags->alt_tapedev= stralloc(s);
        }
-       else if(strncmp(buf, "HOST=", 5) == 0) {
-           match_list->hostname = stralloc(buf+5);
+       else if(strncmp_const_skip(buf, "HOST=", s, ch) == 0) {
+           match_list->hostname = stralloc(s);
        }
-       else if(strncmp(buf, "DISK=", 5) == 0) {
-           match_list->diskname = stralloc(buf+5);
+       else if(strncmp_const_skip(buf, "DISK=", s, ch) == 0) {
+           match_list->diskname = stralloc(s);
        }
-       else if(strncmp(buf, "DATESTAMP=", 10) == 0) {
-           match_list->datestamp = stralloc(buf+10);
+       else if(strncmp_const_skip(buf, "DATESTAMP=", s, ch) == 0) {
+           match_list->datestamp = stralloc(s);
        }
-       else if(strncmp(buf, "END", 3) == 0) {
+       else if(strncmp_const(buf, "END") == 0) {
            re_end = 1;
        }
-       else if(strncmp(buf, "CONFIG=", 7) == 0) {
-           re_config = stralloc(buf+7);
+       else if(strncmp_const_skip(buf, "CONFIG=", s, ch) == 0) {
+           re_config = stralloc(s);
        }
        else if(buf[0] != '\0' && buf[0] >= '0' && buf[0] <= '9') {
 /* XXX does nothing?     amrestore_nargs = atoi(buf); */
            re_end = 1;
        }
-       else {
-       }
-    } while (re_end == 0);
+    }
     amfree(buf);
 
     if(!tapes && rst_flags->alt_tapedev){
@@ -394,9 +495,11 @@ char **argv;
            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 ||
@@ -405,18 +508,18 @@ char **argv;
        /* 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;
        use_changer = 1;
     }
 
+    tapedev = getconf_str(CNF_TAPEDEV);
     /* 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) ) ) {
+    if(re_config &&
+       (use_changer || (rst_flags->alt_tapedev && tapedev &&
+                        strcmp(rst_flags->alt_tapedev, tapedev) == 0) ) ) {
        dbprintf(("%s: Locking devices\n", get_pname()));
        parent_pid = getpid();
        atexit(cleanup);
@@ -431,69 +534,95 @@ char **argv;
     /* 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;
        }
+       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;
+       }
     }
     else {
        rst_flags->pipe_to_fd = fileno(stdout);
-        prompt_stream = stderr;
+        cmdout = stderr;
     }
     dbprintf(("%s: Sending output to file descriptor %d\n",
              get_pname(), rst_flags->pipe_to_fd));
-    
-    
+
+
+    tapedev = getconf_str(CNF_TAPEDEV);
+    if(get_lock == 0 &&
+       re_config && 
+       (use_changer || (rst_flags->alt_tapedev && tapedev &&
+                        strcmp(rst_flags->alt_tapedev, tapedev) == 0) ) ) {
+       send_message(cmdout, 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(cmdout, rst_flags, their_features,
+                    "restore flags are crazy");
        exit(1);
     }
-    
+
     /* actual restoration */
-    search_tapes(prompt_stream, use_changer, tapes, match_list, rst_flags,
+    search_tapes(cmdout, cmdin, 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);
@@ -515,4 +644,3 @@ cleanup(void)
        if(get_lock) unlink(rst_conf_logfile);
     }
 }
-