This patch lets the user set the environment variable DISTCC_TIMEOUT to
control how long distcc will wait for the dns lookup and connection.
Without this patch, distcc would wait for several minutes when a host or
a dns server was down.
I've made the patch as unintrusive as I could (it falls back to the old
behavior when DISTCC_TIMEOUT isn't set), so perhaps you want to change
it to set a default timeout that's shorter than what the system provies.
Do we really want to trust our compile jobs to a host that it takes e.g.
20 seconds to connect to?

Timing out the connect call could be done easier than this, just by
interrupting it with a SIGALRM, but that's not enough to abort
gethostbyname. This method of longjmp'ing from a signal handler is what
they use in curl, so it should be ok.

/Jonas Jensen
diff -ur distcc/src/clinet.c distcc-timeout/src/clinet.c
--- distcc/src/clinet.c	2002-10-07 03:35:19.000000000 +0200
+++ distcc-timeout/src/clinet.c	2002-10-26 20:45:16.000000000 +0200
@@ -37,6 +37,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <signal.h>
+#include <setjmp.h>
 
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -74,8 +75,6 @@
 
 /**
  * Open a socket to a tcp remote host with the specified port.
- *
- * @todo Don't try for too long to connect. 
  **/
 int dcc_open_socket_out(const char *host, int port, int *p_fd)
 {
@@ -94,10 +93,29 @@
 	return EXIT_CONNECT_FAILED;
     }
 
+    /* Limit the time the entire connect/resolve operation can take */
+    {
+	int timeout;
+	char *tmp;
+	jmp_buf env;
+
+	tmp = getenv("DISTCC_TIMEOUT");
+
+	if (tmp && (timeout = atoi(tmp)) > 0) {
+	    if (setjmp(env)) {
+		rs_log_error("timed out connecting to %s", host);
+		(void) close(fd);
+		return EXIT_CONNECT_FAILED;
+	    }
+	    timeout_operation(timeout, &env);
+	}
+    }
+
     hp = gethostbyname(host);
     if (!hp) {
 	rs_log_error("unknown host: \"%s\"", host);
 	(void) close(fd);
+	alarm(0);
 	return EXIT_CONNECT_FAILED;
     }
 
@@ -109,8 +127,10 @@
         rs_log_error("failed to connect to %s port %d: %s", host, port, 
                      strerror(errno));
 	(void) close(fd);
+	alarm(0);
 	return EXIT_CONNECT_FAILED;
     }
+    alarm(0);
 
     rs_trace("client got connection to %s port %d on fd%d", host, port, fd);
 
diff -ur distcc/src/distcc.c distcc-timeout/src/distcc.c
--- distcc/src/distcc.c	2002-10-18 02:36:05.000000000 +0200
+++ distcc-timeout/src/distcc.c	2002-10-26 20:18:11.000000000 +0200
@@ -117,6 +117,7 @@
 "   DISTCC_VERBOSE=1           give debug messages\n"
 "   DISTCC_LOG=LOGFILE         send messages here, not stderr\n"
 "   DISTCC_TCP_CORK=0          disable TCP corks\n"
+"   DISTCC_TIMEOUT=0           TCP connection timeout, in seconds\n"
 "\n"
 "Host specifications:\n"
 "   localhost                  run in place\n"
diff -ur distcc/src/util.c distcc-timeout/src/util.c
--- distcc/src/util.c	2002-09-14 01:51:53.000000000 +0200
+++ distcc-timeout/src/util.c	2002-10-26 20:43:10.000000000 +0200
@@ -31,6 +31,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <signal.h>
+#include <setjmp.h>
 
 #include <sys/stat.h>
 #include <sys/resource.h>
@@ -48,6 +49,8 @@
 			 * Ophir.
 			 *		-- Isaiah 13:12 */
 
+static void timeout_operation_handler(int number);
+static jmp_buf jmpbuf;
 
 int dcc_exit(int exitcode)
 {
@@ -217,3 +220,19 @@
     }
     return 0;
 }
+
+static void timeout_operation_handler(int UNUSED(whatsig))
+{
+    longjmp(jmpbuf, 1);
+}
+
+/**
+ * Starts a timer that will longjmp back to @p buf when it expires
+ **/
+void timeout_operation(int seconds, jmp_buf *buf)
+{
+    memcpy(&jmpbuf, buf, sizeof(jmpbuf));
+    signal(SIGALRM, timeout_operation_handler);
+    alarm(seconds);
+}
+
diff -ur distcc/src/util.h distcc-timeout/src/util.h
--- distcc/src/util.h	2002-09-11 07:37:51.000000000 +0200
+++ distcc-timeout/src/util.h	2002-10-26 20:42:25.000000000 +0200
@@ -21,6 +21,8 @@
  * USA
  */
 
+#include <setjmp.h>
+
 /* util.c */
 int argv_contains(char **argv, const char *s);
 int dcc_redirect_fd(int, const char *fname, int);
@@ -31,6 +33,7 @@
 int set_cloexec_flag (int desc, int value);
 int dcc_ignore_sigpipe(int val);
 int dcc_remove_if_exists(const char *fname);
+void timeout_operation(int seconds, jmp_buf *buf);
 
 #define str_equal(a, b) (!strcmp((a), (b)))
 

Reply via email to