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: