[distcc] Updated patch

Oscar Esteban flesteban at mi.madritel.es
Mon Oct 7 19:44:01 GMT 2002


After rolling back the multi-record dns searching, the patch becomes
smaller (as it was). Here it goes:

diff -wur /home/oscar/cvs_working/distcc/src/clinet.c ./src/clinet.c
--- /home/oscar/cvs_working/distcc/src/clinet.c	Mon Oct  7 21:38:48 2002
+++ ./src/clinet.c	Mon Oct  7 21:29:59 2002
@@ -37,10 +37,12 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <signal.h>
+#include <setjmp.h>
 
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/time.h>
 
 #include <netinet/in.h>
 #include <netinet/tcp.h>
@@ -71,6 +73,72 @@
  **/
 
 
+/**
+ * Simulate connect behaviour, but adding a timeout. If the connection is not
+ * ended in this time, return ETIMEDOUT
+ **/
+static int dcc_connect_tout (int fd, struct sockaddr *addr, int size, int time_to_wait)
+{
+  int got_an_error = 0;
+  int fcntl_previous;
+
+  /* Keep flags associated with the fd */
+  fcntl_previous = fcntl(fd, F_GETFL, 0);
+
+  /* Set the fd to be non-blocking */
+  fcntl(fd, F_SETFL, fcntl_previous | O_NONBLOCK);
+
+  if (connect(fd, addr, size)) {
+    /* If got out in a hurry, but hasn't yet finished... */
+    if (EINPROGRESS == errno) {
+      struct timeval tv;
+      fd_set wfds;
+      int valret;
+
+      FD_ZERO(&wfds);       /* Clear write filedescriptor set */
+      FD_SET(fd, &wfds);    /* Add fd to the set */
+      tv.tv_sec  = time_to_wait / 1000;       /* Set time to wait */
+      tv.tv_usec = 1000 * (time_to_wait % 1000);
+
+      valret = select(fd+1, NULL, &wfds, NULL, &tv);
+      if (valret) {
+        /* We got a result in time */
+
+        int lbuf = sizeof(valret);
+
+        /* Now, let's obtain the real error */
+        getsockopt(fd, SOL_SOCKET, SO_ERROR, &valret, &lbuf);
+        if (valret) {
+          got_an_error = -1;
+
+          errno = valret; /* Do as real connect would have done */
+        }
+      } else {
+        /* Time has passed with no result.
+           TODO: What happens to the pending connection if the fd is
+                 closed?
+        */
+        rs_log_error("connect timed out on fd%d\n", fd);
+        /* We'll fake some error code into errno, TIMEOUT seems ok */
+        errno = ETIMEDOUT;
+        got_an_error = -1;
+      }
+    } else {
+      got_an_error = -1;
+    }
+  }
+
+  /* Restore flags saved before */
+  fcntl(fd, F_SETFL, fcntl_previous);
+
+  return got_an_error;
+}
+
+static jmp_buf time_to_wakeup;
+static void dcc_alarm_handler()
+{
+  longjmp(time_to_wakeup, 1);
+}
 
 /**
  * Open a socket to a tcp remote host with the specified port.
@@ -83,6 +151,7 @@
     struct sockaddr_in sock_out;
     int fd;
     struct hostent *hp;
+    void *prev_signal;
 
     /* Ignore SIGPIPE; we consistently check error codes and will
      * see the EPIPE. */
@@ -94,18 +163,37 @@
 	return EXIT_CONNECT_FAILED;
     }
 
+    alarm(0); /* Let's be sure we do not get a signal before setjmp */
+
+    prev_signal = signal(SIGALRM, dcc_alarm_handler);
+
+    if (!setjmp(time_to_wakeup)) {
+      /* we came here from the setjmp, proceed normally */
+      alarm(DNS_TIMEOUT / 1000); /* Program the alarm */
     hp = gethostbyname(host);
+      alarm(0);             /* Finished in time, disconnect the alarm */
+      signal(SIGALRM, prev_signal); /* and restore previous behaviour */
+
     if (!hp) {
 	rs_log_error("unknown host: \"%s\"", host);
 	(void) close(fd);
 	return EXIT_CONNECT_FAILED;
     }
+    } else {
+      /* we came here from a longjump -> we got the SIGALRM */
+      signal(SIGALRM, prev_signal); /* restore previous behaviour */
+
+      rs_log_error("timeout looking for host: \"%s\"\n", host);
+      (void) close(fd);
+      return EXIT_CONNECT_FAILED;
+    }
 
     memcpy(&sock_out.sin_addr, hp->h_addr, (size_t) hp->h_length);
     sock_out.sin_port = htons((in_port_t) port);
     sock_out.sin_family = PF_INET;
 
-    if (connect(fd, (struct sockaddr *) &sock_out, (int) sizeof(sock_out))) {
+    if (dcc_connect_tout(fd, (struct sockaddr *) &sock_out,
+                         (int) sizeof(sock_out), CONNECT_TIMEOUT)) {
         rs_log_error("failed to connect to %s port %d: %s", host, port, 
                      strerror(errno));
 	(void) close(fd);
diff -wur /home/oscar/cvs_working/distcc/src/clinet.h ./src/clinet.h
--- /home/oscar/cvs_working/distcc/src/clinet.h	Mon Oct  7 21:38:48 2002
+++ ./src/clinet.h	Mon Oct  7 21:32:17 2002
@@ -21,4 +21,8 @@
  * USA
  */
 
+#define CONNECT_TIMEOUT 2000   /* Time given in milliseconds */
+#define DNS_TIMEOUT     2000   /* Time given in milliseconds, but
+				  it'd better be multiple of 1000 */
+
 int dcc_open_socket_out(const char *host, int port, int *fd);
diff -wur /home/oscar/cvs_working/distcc/test/testdistcc.py ./test/testdistcc.py
--- /home/oscar/cvs_working/distcc/test/testdistcc.py	Mon Oct  7 21:38:48 2002
+++ ./test/testdistcc.py	Mon Oct  7 21:11:58 2002
@@ -720,6 +720,79 @@
     def tearDown(self):
         self.leaveRundir()
 
+class dcc_connect_timeout(CompileHello_Case):
+    "base for dcc_connect_timeout test cases"
+
+    def setUp(self, host_list):
+        CompileHello_Case.setUp(self)
+
+        os.environ['DISTCC_HOSTS'] = host_list
+        self.distcc_log = 'distcc.log'
+        os.environ['DISTCC_LOG'] = self.distcc_log
+
+    def runTest(self, string_to_match):
+        self.run_cmd("distcc gcc -c -o testtmp.o testtmp.c")
+        msgs = open(self.distcc_log, 'r').read()
+        self.assertReMatch(string_to_match, msgs)            
+
+"The case 'got connection' is tested in every other test"
+
+class Connect_Dead_Host_Case(dcc_connect_timeout):
+    """Known host, but it does not answer"""
+
+    def GetHostAddress(self):
+        output = self.run_cmd("/sbin/ifconfig").split('\n')
+
+        skipping = 0
+        for line in output:
+            if re.match(r'^lo\s+', line):
+                skipping = 1
+                continue
+            if skipping:
+                if re.match(r'^$', line):
+                    skipping = 0
+                continue
+            "TODO: adapt for IPv6 notation if different"
+            result = re.search(r'addr:(\d+(?:\.\d+)*).*Mask:(\d+(?:\.\d+)*)',
+                               line, re.I)
+
+            if result:
+                """Extract address and mask, and make up an address,
+                   hoping that it does not exist. Any better idea?"""
+                addr = map(int, result.group(1).split("."))
+                mask = map(int, result.group(2).split("."))
+                for i in xrange(len(addr)):
+                    addr[i] ^= (0x85 & ~mask[i])
+                        
+                return ".".join(map(repr, addr))
+
+        return ""
+
+    def setUp(self):
+        self.host_list = self.GetHostAddress()
+        dcc_connect_timeout.setUp(self, self.host_list)
+    
+    def runTest(self):
+        assert self.host_list != "", "Can not find a network interface excluding localhost"
+        dcc_connect_timeout.runTest(self, r'connect timed out')
+
+class Connect_Invalid_Port_Case(dcc_connect_timeout):
+    """Known host, but closed port
+       It should be nice to check for 'connection refused', but due to
+       localization, the message will be different in every case."""
+    def setUp(self):
+        dcc_connect_timeout.setUp(self, 'localhost:9999')
+    
+    def runTest(self):
+        dcc_connect_timeout.runTest(self, r'failed to connect')
+
+class Connect_Invalid_Server_Name_Case(dcc_connect_timeout):
+    """Invalid server name"""
+    def setUp(self):
+        dcc_connect_timeout.setUp (self, 'no.such.host.here')
+    
+    def runTest(self):
+        dcc_connect_timeout.runTest(self, r'timeout looking for host')
         
 class ImpliedOutput_Case(CompileHello_Case):
     """Test handling absence of -o"""
@@ -908,4 +981,7 @@
                              ModeBits_Case,
                              Concurrent_Case,
                              ThousandFold_Case,
+			     Connect_Dead_Host_Case,
+			     Connect_Invalid_Port_Case,
+			     Connect_Invalid_Server_Name_Case,
                              BigAssFile_Case])




More information about the distcc mailing list