[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#668948: apt: Proposal for a better handling of timeouted connection



Package: apt
Version: 0.9.3
Followup-For: Bug #668948

Any comments on my proposal? In any case, here is an updated patch
for apt 0.9.3.

  Regards,
    Vincent

-- Package-specific info:

-- /etc/apt/preferences --


-- (/etc/apt/sources.list present, but not submitted) --


-- System Information:
Debian Release: wheezy/sid
  APT prefers unstable
  APT policy: (500, 'unstable'), (500, 'testing'), (500, 'stable'), (1, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 3.2.0-2-amd64 (SMP w/8 CPU cores)
Locale: LANG=fr_FR.UTF-8, LC_CTYPE=fr_FR.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages apt depends on:
ii  debian-archive-keyring  2012.2
ii  gnupg                   1.4.12-4
ii  libapt-pkg4.12          0.9.3+nmu1
ii  libc6                   2.13-32
ii  libgcc1                 1:4.7.0-8
ii  libstdc++6              4.7.0-8

apt recommends no packages.

Versions of packages apt suggests:
ii  apt-doc     <none>
ii  aptitude    0.6.7-1
ii  dpkg-dev    1.16.3+0~1333384407.7~1.gbp45fe25
ii  python-apt  0.8.4
ii  synaptic    0.75.10
ii  xz-utils    5.1.1alpha+20110809-3

-- no debconf information
diff --git a/methods/connect.cc b/methods/connect.cc
index 9a092a4..c04ccb0 100644
--- a/methods/connect.cc
+++ b/methods/connect.cc
@@ -41,6 +41,8 @@ static std::string LastHost;
 static int LastPort = 0;
 static struct addrinfo *LastHostAddr = 0;
 static struct addrinfo *LastUsed = 0;
+static int nbHosts = 0;
+static int use_parallel_connect = 0;
 
 // Set of IP/hostnames that we timed out before or couldn't resolve
 static std::set<std::string> bad_addr;
@@ -129,6 +131,191 @@ static bool DoConnect(struct addrinfo *Addr,std::string Host,
    return true;
 }
 									/*}}}*/
+// connect_to_host - Connect to a server				/*{{{*/
+// ---------------------------------------------------------------------
+/* Performs a connection to the server */
+#define TIMEOUT 500
+static bool
+connect_to_host(struct addrinfo *StartHost,struct addrinfo *FirstHostAddr,
+		std::string Host,int count,int &Fd,
+		unsigned long TimeOut,pkgAcqMethod * Owner) {
+  struct addrinfo *Addr, *NextAddr, **Addrs = NULL;
+  int fd = -1, n, i, j, max = -1, *fds;
+  struct timeval *timeout, timeout0 = { 0, TIMEOUT * 1000},
+    timeout1 = { 0, 0 };
+  timeout1.tv_sec = TimeOut;
+  fd_set fdset, wrset;
+  // Show a status indicator
+  char Name[NI_MAXHOST];
+  char Service[NI_MAXSERV];
+  
+  fds = new int[count]();//calloc(count, sizeof(*fds));
+  if (fds == NULL) {
+    _error->Errno("calloc",_("Cannot allocate %d file descriptors"),count);
+    return false;
+  }
+  Addrs = new struct addrinfo *[count]();
+  if (Addrs == NULL) {
+    _error->Errno("calloc",_("Cannot allocate %d pointers"),count);
+    free(fds);
+    return false;
+  }
+  FD_ZERO(&fdset);
+  for(Addr = StartHost, i = 0, count = 0; Addr != NULL; Addr = NextAddr) {
+    NextAddr = Addr;
+  
+    // Ignore UNIX domain sockets
+    do {
+      NextAddr = NextAddr->ai_next;
+    } while (NextAddr != 0 && NextAddr->ai_family == AF_UNIX);
+
+    /* If we reached the end of the search list then wrap around to the
+       start */
+    if (NextAddr == 0)
+	NextAddr = FirstHostAddr;
+      
+    // Reached the end of the search cycle
+    if (NextAddr == StartHost)
+      NextAddr = NULL;
+
+
+    Name[0] = 0;
+    Service[0] = 0;
+    getnameinfo(Addr->ai_addr,Addr->ai_addrlen,
+		Name,sizeof(Name),Service,sizeof(Service),
+		NI_NUMERICHOST|NI_NUMERICSERV);
+    Owner->Status(_("Connecting to %s (%s)"),Host.c_str(),Name);
+
+    fd = socket(Addr->ai_family, Addr->ai_socktype, Addr->ai_protocol);
+    if (fd == -1) {
+      /*
+       * If AI_ADDRCONFIG is not supported we will get
+       * EAFNOSUPPORT returned.  Behave as if the address
+       * was not there.
+       */
+      if (errno != EAFNOSUPPORT) {
+	_error->Errno("socket",_("Could not create a socket for %s (f=%u t=%u p=%u)"),
+		      Name,Addr->ai_family,Addr->ai_socktype,Addr->ai_protocol);
+      } else if (NextAddr != NULL)
+	continue;
+    } else if (fd >= FD_SETSIZE) {
+      close(fd);
+    } else {
+      SetNonBlock(fd,true);
+      if (connect(fd, Addr->ai_addr, Addr->ai_addrlen) == -1) {
+	if (errno != EINPROGRESS) {
+	  _error->Errno("connect",_("Cannot initiate the connection "
+				    "to %s:%s (%s)."),Host.c_str(),Service,Name);
+	  close(fd);
+	} else {
+	  /*
+	   * Record the information for this descriptor.
+	   */
+	  fds[i] = fd;
+	  Addrs[i] = Addr;
+	  FD_SET(fd, &fdset);
+	  if (max == -1 || fd > max)
+	    max = fd;
+	  count++;
+	  i++;
+	}
+      } else  {
+	/*
+	 * We connected without blocking.
+	 */
+	LastUsed = Addr;
+	goto done;
+      }
+    }
+
+    if (count == 0)
+      continue;
+    
+    //assert(max != -1);
+    do {
+      if (Addr->ai_next != NULL)
+	timeout = &timeout0;
+      else
+	timeout = &timeout1;
+      
+      /* The write bit is set on both success and failure. */
+      wrset = fdset;
+      n = select(max + 1, NULL, &wrset, NULL, timeout);
+      if (n == 0) {
+	timeout0.tv_usec >>= 1;
+	break;
+      }
+      if (n < 0) {
+	if (errno == EAGAIN || errno == EINTR)
+	  continue;
+	_error->Errno("select", _("Cannot select"));
+	fd = -1;
+	goto done;
+      }
+      for (fd = 0; fd <= max; fd++) {
+	if (FD_ISSET(fd, &wrset)) {
+	  socklen_t len;
+	  int err;
+	  for (j = 0; j < i; j++)
+	    if (fds[j] == fd)
+	      break;
+	  //assert(j < i);
+	  /*
+	   * Test to see if the connect
+	   * succeeded.
+	   */
+	  len = sizeof(err);
+	  n = getsockopt(fd, SOL_SOCKET,
+			 SO_ERROR, &err, &len);
+	  if (n != 0 || err != 0) {
+	    if (n != 0) {
+	      _error->Errno("getsockopt",_("Failed"));
+	    } else {
+	      errno = err;
+	      if(errno == ECONNREFUSED)
+		Owner->SetFailReason("ConnectionRefused");
+	      else if (errno == ETIMEDOUT)
+		Owner->SetFailReason("ConnectionTimedOut");
+	      _error->Errno("connect",_("Could not connect to %s:%s (%s)."),
+			    Host.c_str(),Service,Name);
+	    }
+	    close(fd);
+	    FD_CLR(fd, &fdset);
+	    fds[j] = -1;
+	    count--;
+	    continue;
+	  }
+	  /* Connect succeeded. */
+	  LastUsed = Addrs[j];
+	  goto done;
+	}
+      }
+    } while (timeout == &timeout1 && count != 0);
+  }
+  
+  /* We failed to connect. */
+  fd = -1;
+  
+ done:
+  /* Close all other descriptors we have created. */
+  for (j = 0; j < i; j++)
+    if (fds[j] != fd && fds[j] != -1) {
+      close(fds[j]);
+    }
+  
+  /* Free everything. */
+  if (fds) delete[](fds);
+  if (Addrs) delete[](Addrs);
+  
+  if (fd != -1) {
+    Fd=fd;
+    _error->Discard();
+    return true;
+  }
+
+  return false;
+}
+									/*}}}*/
 // Connect - Connect to a server					/*{{{*/
 // ---------------------------------------------------------------------
 /* Performs a connection to the server */
@@ -158,6 +345,7 @@ bool Connect(std::string Host,int Port,const char *Service,int DefPort,int &Fd,
 	 freeaddrinfo(LastHostAddr);
 	 LastHostAddr = 0;
 	 LastUsed = 0;
+	 nbHosts = 0;
       }
       
       // We only understand SOCK_STREAM sockets.
@@ -205,6 +393,17 @@ bool Connect(std::string Host,int Port,const char *Service,int DefPort,int &Fd,
       
       LastHost = Host;
       LastPort = Port;
+
+      /*
+       * Work out how many possible IP we could use.
+       */
+      struct addrinfo *res;
+      for (res = LastHostAddr; res; res = res->ai_next) {
+	// Ignore UNIX domain sockets
+	if (res->ai_family == AF_UNIX)
+	  continue;
+	nbHosts++;
+      }
    }
 
    // When we have an IP rotation stay with the last IP.
@@ -212,6 +411,15 @@ bool Connect(std::string Host,int Port,const char *Service,int DefPort,int &Fd,
    if (LastUsed != 0)
        CurHost = LastUsed;
    
+   if (use_parallel_connect == 0) {
+     use_parallel_connect=1;
+     if (getenv("APT_SEQUENTIAL_CONNECT") != NULL)
+       use_parallel_connect=-1;
+   }
+   if (use_parallel_connect == 1) {
+     return connect_to_host(CurHost,LastHostAddr,Host,nbHosts,Fd,TimeOut,Owner);
+   }
+
    while (CurHost != 0)
    {
       if (DoConnect(CurHost,Host,TimeOut,Fd,Owner) == true)

Reply to: