From f3d3a50a5697957befaf94cb44797234a68068c3 Mon Sep 17 00:00:00 2001 From: hniksic Date: Sat, 13 Apr 2002 15:18:28 -0700 Subject: [PATCH] [svn] Generalize connect_with_timeout into run_with_timeout. Published in . --- src/ChangeLog | 11 ++++++ src/connect.c | 99 +++++++++++---------------------------------------- src/host.c | 4 +++ src/utils.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++ src/utils.h | 2 ++ 5 files changed, 128 insertions(+), 79 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index 12912cf6..e7470ac0 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,14 @@ +2002-04-14 Hrvoje Niksic + + * connect.c (connect_with_timeout): Firing SIGALRM can result in + connect() exiting with EINTR. Treat EINTR the same as ETIMEDOUT. + +2002-04-13 Hrvoje Niksic + + * connect.c (connect_with_timeout): Use it. + + * utils.c (run_with_timeout): New function. + 2002-04-13 Hrvoje Niksic * url.c (getproxy): Accept a struct url argument. This obviates diff --git a/src/connect.c b/src/connect.c index d95ad74f..29a8b6e0 100644 --- a/src/connect.c +++ b/src/connect.c @@ -47,29 +47,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ # include #endif /* HAVE_SYS_SELECT_H */ -/* To implement connect with timeout, we need signal and either - sigsetjmp or sigblock. */ -#undef UNIX_CONNECT_TIMEOUT -#ifdef HAVE_SIGNAL_H -# include -#endif -#ifdef HAVE_SETJMP_H -# include -#endif -/* If sigsetjmp is a macro, configure won't pick it up. */ -#ifdef sigsetjmp -# define HAVE_SIGSETJMP -#endif -#ifdef HAVE_SIGNAL -# ifdef HAVE_SIGSETJMP -# define UNIX_CONNECT_TIMEOUT -# endif -# ifdef HAVE_SIGBLOCK -# define UNIX_CONNECT_TIMEOUT -# endif -#endif - #include "wget.h" +#include "utils.h" #include "host.h" #include "connect.h" @@ -107,45 +86,19 @@ resolve_bind_address (void) bind_address_resolved = 1; } -#ifndef UNIX_CONNECT_TIMEOUT -static int -connect_with_timeout (int fd, const struct sockaddr *addr, int addrlen, - int timeout) +struct cwt_context { + int fd; + const struct sockaddr *addr; + int addrlen; + int result; +}; + +static void +connect_with_timeout_callback (void *arg) { - return connect (fd, addr, addrlen); + struct cwt_context *ctx = (struct cwt_context *)arg; + ctx->result = connect (ctx->fd, ctx->addr, ctx->addrlen); } -#else /* UNIX_CONNECT_TIMEOUT */ -/* Implementation of connect with timeout. */ - -#ifdef HAVE_SIGSETJMP -#define SETJMP(env) sigsetjmp (env, 1) - -static sigjmp_buf abort_connect_env; - -RETSIGTYPE -abort_connect (int ignored) -{ - siglongjmp (abort_connect_env, -1); -} -#else /* not HAVE_SIGSETJMP */ -#define SETJMP(env) setjmp (env) - -static jmp_buf abort_connect_env; - -RETSIGTYPE -abort_connect (int ignored) -{ - /* We don't have siglongjmp to preserve the set of blocked signals; - if we longjumped out of the handler at this point, SIGALRM would - remain blocked. We must unblock it manually. */ - int mask = siggetmask (); - mask &= ~sigmask(SIGALRM); - sigsetmask (mask); - - /* Now it's safe to longjump. */ - longjmp (abort_connect_env, -1); -} -#endif /* not HAVE_SIGSETJMP */ /* Like connect, but specifies a timeout. If connecting takes longer than TIMEOUT seconds, -1 is returned and errno is set to @@ -155,32 +108,20 @@ static int connect_with_timeout (int fd, const struct sockaddr *addr, int addrlen, int timeout) { - int result, saved_errno; + struct cwt_context ctx; + ctx.fd = fd; + ctx.addr = addr; + ctx.addrlen = addrlen; - if (timeout == 0) - return connect (fd, addr, addrlen); - - signal (SIGALRM, abort_connect); - if (SETJMP (abort_connect_env) != 0) + if (run_with_timeout (timeout, connect_with_timeout_callback, &ctx)) { - /* Longjumped out of connect with a timeout. */ - signal (SIGALRM, SIG_DFL); errno = ETIMEDOUT; return -1; } - - alarm (timeout); - result = connect (fd, addr, addrlen); - - saved_errno = errno; /* In case alarm() or signal() change - errno. */ - alarm (0); - signal (SIGALRM, SIG_DFL); - errno = saved_errno; - - return result; + if (ctx.result == -1 && errno == EINTR) + errno = ETIMEDOUT; + return ctx.result; } -#endif /* UNIX_CONNECT_TIMEOUT */ /* A kludge, but still better than passing the host name all the way to connect_to_one. */ diff --git a/src/host.c b/src/host.c index dbe11edb..45455bf0 100644 --- a/src/host.c +++ b/src/host.c @@ -551,6 +551,10 @@ lookup_host (const char *host, int silent) if (!silent) logprintf (LOG_VERBOSE, _("Resolving %s... "), host); + /* Host name lookup goes on below. #### We should implement + getaddrinfo_with_timeout and gethostbyname_with_timeout the same + way connect.c implements connect_with_timeout. */ + #ifdef INET6 { struct addrinfo hints, *ai; diff --git a/src/utils.c b/src/utils.c index ca8505ac..cd1e645d 100644 --- a/src/utils.c +++ b/src/utils.c @@ -59,6 +59,27 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ # include #endif +/* Needed for run_with_timeout. */ +#undef USE_SIGNAL_TIMEOUT +#ifdef HAVE_SIGNAL_H +# include +#endif +#ifdef HAVE_SETJMP_H +# include +#endif +/* If sigsetjmp is a macro, configure won't pick it up. */ +#ifdef sigsetjmp +# define HAVE_SIGSETJMP +#endif +#ifdef HAVE_SIGNAL +# ifdef HAVE_SIGSETJMP +# define USE_SIGNAL_TIMEOUT +# endif +# ifdef HAVE_SIGBLOCK +# define USE_SIGNAL_TIMEOUT +# endif +#endif + #include "wget.h" #include "utils.h" #include "fnmatch.h" @@ -1757,3 +1778,73 @@ debug_test_md5 (char *buf) return res; } #endif + +/* Implementation of run_with_timeout, a generic timeout handler for + systems with Unix-like signal handling. */ +#ifdef HAVE_SIGSETJMP +#define SETJMP(env) sigsetjmp (env, 1) + +static sigjmp_buf run_with_timeout_env; + +static RETSIGTYPE +abort_run_with_timeout (int sig) +{ + assert (sig == SIGALRM); + siglongjmp (run_with_timeout_env, -1); +} +#else /* not HAVE_SIGSETJMP */ +#define SETJMP(env) setjmp (env) + +static jmp_buf run_with_timeout_env; + +static RETSIGTYPE +abort_run_with_timeout (int sig) +{ + assert (sig == SIGALRM); + /* We don't have siglongjmp to preserve the set of blocked signals; + if we longjumped out of the handler at this point, SIGALRM would + remain blocked. We must unblock it manually. */ + int mask = siggetmask (); + mask &= ~sigmask(SIGALRM); + sigsetmask (mask); + + /* Now it's safe to longjump. */ + longjmp (run_with_timeout_env, -1); +} +#endif /* not HAVE_SIGSETJMP */ + +int +run_with_timeout (long timeout, void (*fun) (void *), void *arg) +{ +#ifndef USE_SIGNAL_TIMEOUT + fun (arg); + return 0; +#else + int saved_errno; + + if (timeout == 0) + { + fun (arg); + return 0; + } + + signal (SIGALRM, abort_run_with_timeout); + if (SETJMP (run_with_timeout_env) != 0) + { + /* Longjumped out of FUN with a timeout. */ + signal (SIGALRM, SIG_DFL); + return 1; + } + alarm (timeout); + fun (arg); + + /* Preserve errno in case alarm() or signal() modifies it. */ + saved_errno = errno; + alarm (0); + signal (SIGALRM, SIG_DFL); + errno = saved_errno; + + return 0; +#endif +} + diff --git a/src/utils.h b/src/utils.h index 162ddd70..a941c5f9 100644 --- a/src/utils.h +++ b/src/utils.h @@ -106,4 +106,6 @@ char *html_quote_string PARAMS ((const char *)); int determine_screen_width PARAMS ((void)); int random_number PARAMS ((int)); +int run_with_timeout PARAMS ((long, void (*) (void *), void *)); + #endif /* UTILS_H */