]> git.donarmstrong.com Git - samtools.git/blobdiff - knetfile.c
reduce the chance of blocking in FTP connection
[samtools.git] / knetfile.c
index 9a6878d24da19f0675566b63612d713d6acc035e..12dc2d17af733d2c5bb93870a3285833fdb063cd 100644 (file)
@@ -1,3 +1,4 @@
+#include <time.h>
 #include <stdio.h>
 #include <netdb.h>
 #include <ctype.h>
 #include <sys/socket.h>
 #include "knetfile.h"
 
+static int socket_wait(int fd, int is_read)
+{
+       fd_set fds, *fdr = 0, *fdw = 0;
+       struct timeval tv;
+       int ret;
+       tv.tv_sec = 5; tv.tv_usec = 0; // 5 seconds time out
+       FD_ZERO(&fds);
+       FD_SET(fd, &fds);
+       if (is_read) fdr = &fds;
+       else fdw = &fds;
+       ret = select(fd+1, fdr, fdw, 0, &tv);
+       if (ret == -1) perror("select");
+       return ret;
+}
+
 static int kftp_get_response(knetFile *ftp)
 {
        unsigned char c;
        int n = 0;
        char *p;
+       if (socket_wait(ftp->ctrl_fd, 1) <= 0) return 0;
        while (read(ftp->ctrl_fd, &c, 1)) { // FIXME: this is *VERY BAD* for unbuffered I/O
-//             fputc(c, stderr);
+               //fputc(c, stderr);
                if (n >= ftp->max_response) {
                        ftp->max_response = ftp->max_response? ftp->max_response<<1 : 256;
                        ftp->response = realloc(ftp->response, ftp->max_response);
@@ -35,6 +52,7 @@ static int kftp_get_response(knetFile *ftp)
 
 static int kftp_send_cmd(knetFile *ftp, const char *cmd, int is_get)
 {
+       if (socket_wait(ftp->ctrl_fd, 0) <= 0) return -1; // socket is not ready for writing
        write(ftp->ctrl_fd, cmd, strlen(cmd));
        return is_get? kftp_get_response(ftp) : 0;
 }
@@ -107,16 +125,16 @@ int kftp_connect(knetFile *ftp)
 
 int kftp_reconnect(knetFile *ftp)
 {
-       if (ftp->ctrl_fd) {
+       if (ftp->ctrl_fd >= 0) {
                close(ftp->ctrl_fd);
-               ftp->ctrl_fd = 0;
+               ftp->ctrl_fd = -1;
        }
        close(ftp->fd);
        return kftp_connect(ftp);
 }
 
 // initialize ->type, ->host and ->retr
-knetFile *kftp_prep(const char *fn, const char *mode)
+knetFile *kftp_parse_url(const char *fn, const char *mode)
 {
        knetFile *fp;
        char *p;
@@ -127,6 +145,7 @@ knetFile *kftp_prep(const char *fn, const char *mode)
        l = p - fn - 6;
        fp = calloc(1, sizeof(knetFile));
        fp->type = KNF_TYPE_FTP;
+       fp->fd = -1;
        fp->host = calloc(l + 1, 1);
        if (strchr(mode, 'c')) fp->no_reconnect = 1;
        strncpy(fp->host, fn + 6, l);
@@ -138,7 +157,8 @@ knetFile *kftp_prep(const char *fn, const char *mode)
 // place ->fd at offset off
 int kftp_connect_file(knetFile *fp)
 {
-       if (fp->fd) {
+       int ret;
+       if (fp->fd >= 0) {
                close(fp->fd);
                if (fp->no_reconnect) kftp_get_response(fp);
        }
@@ -150,7 +170,13 @@ int kftp_connect_file(knetFile *fp)
        }
        kftp_send_cmd(fp, fp->retr, 0);
        kftp_pasv_connect(fp);
-       kftp_get_response(fp);
+       ret = kftp_get_response(fp);
+       if (ret != 150) {
+               fprintf(stderr, "[kftp_connect_file] %s\n", fp->response);
+               close(fp->fd);
+               fp->fd = -1;
+               return -1;
+       }
        fp->is_ready = 1;
        return 0;
 }
@@ -163,13 +189,17 @@ knetFile *knet_open(const char *fn, const char *mode)
                return 0;
        }
        if (strstr(fn, "ftp://") == fn) {
-               fp = kftp_prep(fn, mode);
+               fp = kftp_parse_url(fn, mode);
                if (fp == 0) return 0;
                if (kftp_connect(fp) == -1) {
                        knet_close(fp);
                        return 0;
                }
                kftp_connect_file(fp);
+               if (fp->fd < 0) {
+                       knet_close(fp);
+                       return 0;
+               }
        } else {
                int fd = open(fn, O_RDONLY);
                if (fd == -1) {
@@ -194,6 +224,7 @@ knetFile *knet_dopen(int fd, const char *mode)
 off_t knet_read(knetFile *fp, void *buf, off_t len)
 {
        off_t l = 0;
+       if (fp->fd < 0) return 0;
        if (fp->type == KNF_TYPE_LOCAL) {
                l = read(fp->fd, buf, len);
                fp->offset += l;
@@ -205,6 +236,7 @@ off_t knet_read(knetFile *fp, void *buf, off_t len)
                        fp->is_ready = 1;
                }
                while (rest) {
+                       if (socket_wait(fp->fd, 1) <= 0) break; // socket is not ready for reading
                        curr = read(fp->fd, buf + l, rest);
                        if (curr == 0) break; // FIXME: end of file or bad network? I do not know...
                        l += curr; rest -= curr;
@@ -239,8 +271,8 @@ int knet_seek(knetFile *fp, off_t off, int whence)
 int knet_close(knetFile *fp)
 {
        if (fp == 0) return 0;
-       if (fp->ctrl_fd > 0) close(fp->ctrl_fd);
-       if (fp->fd > 0) close(fp->fd);
+       if (fp->ctrl_fd >= 0) close(fp->ctrl_fd);
+       if (fp->fd >= 0) close(fp->fd);
        free(fp->response); free(fp->retr); free(fp->host);
        free(fp);
        return 0;