We already set fetchTimeout to 10 seconds, but this does not apply for some
of the connection setup steps such as connect(). This left us hanging in a
few places where we would normally be stuck in a system call for an
indeterminate amount of time.

This addresses FS#15369, which has grown to include and duplicate several
other bugs and failures such as specifying a non-existent FTP server, DSL
disconnections, etc.

Signed-off-by: Dan McGee <[email protected]>
---

Anyone that wants to look this over, it was pretty simple and kills one of our
long-lingering issues before a 3.5.0 release.

The output isn't necessarily beautiful, but it at least doesn't hang forever
now. I tested this by adding a line like
    Server = ftp://192.168.4.4/$repo/os/$arch
to my /etc/pacman.d/mirrorlist file, and then running pacman -Sy.

$ sudo ./src/pacman/pacman -Sy
Password: 
:: Synchronizing package databases...
 testing is up to date
error: failed retrieving file 'core.db' from 192.168.4.4 : Interrupted system 
call
 core is up to date
error: failed retrieving file 'extra.db' from 192.168.4.4 : Interrupted system 
call
 extra is up to date
 community-testing is up to date
error: failed retrieving file 'multilib.db' from 192.168.4.4 : Interrupted 
system call
 multilib is up to date
error: failed retrieving file 'community.db' from 192.168.4.4 : Interrupted 
system call
 community is up to date

-Dan

 lib/libalpm/dload.c |   21 +++++++++++++++++----
 1 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c
index 7a98eb1..44ff17c 100644
--- a/lib/libalpm/dload.c
+++ b/lib/libalpm/dload.c
@@ -88,10 +88,10 @@ static const char *gethost(struct url *fileurl)
 int dload_interrupted;
 static void inthandler(int signum)
 {
-       dload_interrupted = 1;
+       dload_interrupted = signum;
 }
 
-#define check_stop() if(dload_interrupted) { ret = -1; goto cleanup; }
+#define check_stop() do { alarm(0); if(dload_interrupted) { ret = -1; goto 
cleanup; } } while(0)
 enum sighandlers { OLD = 0, NEW = 1 };
 
 static int download_internal(const char *url, const char *localpath,
@@ -102,7 +102,7 @@ static int download_internal(const char *url, const char 
*localpath,
        off_t dl_thisfile = 0;
        ssize_t nread = 0;
        char *tempfile, *destfile, *filename;
-       struct sigaction sig_pipe[2], sig_int[2];
+       struct sigaction sig_pipe[2], sig_int[2], sig_alrm[2];
 
        off_t local_size = 0;
        time_t local_time = 0;
@@ -169,6 +169,12 @@ static int download_internal(const char *url, const char 
*localpath,
        sigaction(SIGINT, NULL, &sig_int[OLD]);
        sigaction(SIGINT, &sig_int[NEW], NULL);
 
+       sig_alrm[NEW].sa_handler = &inthandler;
+       sigemptyset(&sig_alrm[NEW].sa_mask);
+       sig_alrm[NEW].sa_flags = 0;
+       sigaction(SIGALRM, NULL, &sig_alrm[OLD]);
+       sigaction(SIGALRM, &sig_alrm[NEW], NULL);
+
        /* NOTE: libfetch does not reset the error code, be sure to do it before
         * calls into the library */
 
@@ -184,7 +190,9 @@ static int download_internal(const char *url, const char 
*localpath,
         * trouble in trying to do both size and "if-modified-since" logic in a
         * non-stat request, so avoid it. */
        fetchLastErrCode = 0;
+       alarm(fetchTimeout);
        if(fetchStat(fileurl, &ust, "") == -1) {
+               alarm(0);
                pm_errno = PM_ERR_LIBFETCH;
                _alpm_log(PM_LOG_ERROR, _("failed retrieving file '%s' from %s 
: %s\n"),
                                filename, gethost(fileurl), fetchLastErrString);
@@ -211,6 +219,7 @@ static int download_internal(const char *url, const char 
*localpath,
        }
 
        fetchLastErrCode = 0;
+       alarm(fetchTimeout);
        dlf = fetchGet(fileurl, "");
        check_stop();
 
@@ -252,6 +261,7 @@ static int download_internal(const char *url, const char 
*localpath,
                handle->dlcb(filename, 0, ust.size);
        }
 
+       alarm(fetchTimeout);
        while((nread = fetchIO_read(dlf, buffer, PM_DLBUF_LEN)) > 0) {
                check_stop();
                size_t nwritten = 0;
@@ -268,7 +278,9 @@ static int download_internal(const char *url, const char 
*localpath,
                if(handle->dlcb) {
                        handle->dlcb(filename, dl_thisfile, ust.size);
                }
+               alarm(fetchTimeout);
        }
+       alarm(0);
 
        /* did the transfer complete normally? */
        if (nread == -1) {
@@ -333,10 +345,11 @@ cleanup:
        fetchFreeURL(fileurl);
 
        /* restore the old signal handlers */
+       sigaction(SIGALRM, &sig_alrm[OLD], NULL);
        sigaction(SIGINT, &sig_int[OLD], NULL);
        sigaction(SIGPIPE, &sig_pipe[OLD], NULL);
        /* if we were interrupted, trip the old handler */
-       if(dload_interrupted) {
+       if(dload_interrupted == SIGINT) {
                raise(SIGINT);
        }
 
-- 
1.7.4


Reply via email to