Here is the much awaited connect timeout patch. Although the alarm()
implementation should be the simplest and most portable one, there is
still a portability catch: longjumping out of a signal handler will on
some OS'es leave the signal blocked. You must either explicitly
unblock it using sigblock or use sigsetjmp/siglongjmp which save the
signal set in the first place.
To turn this on, configure checks for the availability of signals (all
Unix systems) and the presence of either sigsetjmp (SYSV/POSIX) or
sigblock (BSD). I believe that should cover virtually all Unix
systems in use today.
For Windows we'll have to write a separate connect_with_timeout,
probably in mswindows.c. That one should use whatever is most
portable under Windows, and is left as an excercise for a
Windows-savvy contributor.
ChangeLog:
2002-04-12 Hrvoje Niksic <[EMAIL PROTECTED]>
* configure.in: Check for <setjmp.h>. Check for sigsetjmp and
sigblock.
src/ChangeLog:
2002-04-12 Hrvoje Niksic <[EMAIL PROTECTED]>
* connect.c (connect_with_timeout): New function.
(connect_to_one): Use it.
* config.h.in: Add stubs for HAVE_SIGSETJMP, HAVE_SIGBLOCK, and
HAVE_SETJMP_H.
Index: configure.in
===================================================================
RCS file: /pack/anoncvs/wget/configure.in,v
retrieving revision 1.32
diff -u -r1.32 configure.in
--- configure.in 2001/12/12 08:30:03 1.32
+++ configure.in 2002/04/12 01:12:24
@@ -156,7 +156,8 @@
dnl Checks for headers
dnl
AC_CHECK_HEADERS(string.h stdarg.h unistd.h sys/time.h utime.h sys/utime.h pwd.h)
-AC_CHECK_HEADERS(termios.h sys/ioctl.h sys/select.h sys/utsname.h signal.h)
+AC_CHECK_HEADERS(termios.h sys/ioctl.h sys/select.h sys/utsname.h)
+AC_CHECK_HEADERS(signal.h setjmp.h)
AC_HEADER_TIME
dnl
@@ -174,8 +175,8 @@
AC_FUNC_ALLOCA
AC_FUNC_MMAP
AC_CHECK_FUNCS(strdup strstr strcasecmp strncasecmp strpbrk memmove)
-AC_CHECK_FUNCS(gettimeofday mktime strptime)
-AC_CHECK_FUNCS(strerror snprintf vsnprintf select signal symlink access isatty)
+AC_CHECK_FUNCS(gettimeofday mktime strptime strerror snprintf vsnprintf)
+AC_CHECK_FUNCS(select sigblock sigsetjmp signal symlink access isatty)
AC_CHECK_FUNCS(uname gethostname usleep)
dnl
Index: src/config.h.in
===================================================================
RCS file: /pack/anoncvs/wget/src/config.h.in,v
retrieving revision 1.20
diff -u -r1.20 config.h.in
--- src/config.h.in 2001/12/06 10:45:27 1.20
+++ src/config.h.in 2002/04/12 01:12:24
@@ -165,6 +165,12 @@
/* Define if you have the signal function. */
#undef HAVE_SIGNAL
+/* Define if you have the sigsetjmp function. */
+#undef HAVE_SIGSETJMP
+
+/* Define if you have the sigblock function. */
+#undef HAVE_SIGBLOCK
+
/* Define if you have the gettext function. */
#undef HAVE_GETTEXT
@@ -200,6 +206,9 @@
/* Define if you have the <signal.h> header file. */
#undef HAVE_SIGNAL_H
+
+/* Define if you have the <setjmp.h> header file. */
+#undef HAVE_SETJMP_H
/* Define if you have the <libintl.h> header file. */
#undef HAVE_LIBINTL_H
Index: src/connect.c
===================================================================
RCS file: /pack/anoncvs/wget/src/connect.c,v
retrieving revision 1.13
diff -u -r1.13 connect.c
--- src/connect.c 2002/01/25 03:34:23 1.13
+++ src/connect.c 2002/04/12 01:12:24
@@ -47,6 +47,28 @@
# include <sys/select.h>
#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 <signal.h>
+#endif
+#ifdef HAVE_SETJMP_H
+# include <setjmp.h>
+#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 "host.h"
#include "connect.h"
@@ -84,7 +106,82 @@
address_list_release (al);
bind_address_resolved = 1;
}
+
+#ifndef UNIX_CONNECT_TIMEOUT
+static int
+connect_with_timeout (int fd, const struct sockaddr *addr, int addrlen,
+ int timeout)
+{
+ return connect (fd, addr, 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
+ ETIMEDOUT. */
+
+static int
+connect_with_timeout (int fd, const struct sockaddr *addr, int addrlen,
+ int timeout)
+{
+ int result, saved_errno;
+
+ if (timeout == 0)
+ return connect (fd, addr, addrlen);
+ signal (SIGALRM, abort_connect);
+ if (SETJMP (abort_connect_env) != 0)
+ {
+ /* 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;
+}
+#endif /* UNIX_CONNECT_TIMEOUT */
+
/* A kludge, but still better than passing the host name all the way
to connect_to_one. */
static const char *connection_host_name;
@@ -142,7 +239,7 @@
}
/* Connect the socket to the remote host. */
- if (connect (sock, &sa.sa, sockaddr_len ()) < 0)
+ if (connect_with_timeout (sock, &sa.sa, sockaddr_len (), opt.timeout) < 0)
{
close (sock);
sock = -1;