Make creation of statistics collection socket more robust, by allowing it
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 15 Nov 2003 17:24:07 +0000 (17:24 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 15 Nov 2003 17:24:07 +0000 (17:24 +0000)
to try additional addresses returned from getaddrinfo() if the first one
fails at the bind() or connect() steps.  Per yesterday's discussion.

src/backend/postmaster/pgstat.c

index 2539c32d6542fb6a3a4904ca185115ef38a88b86..590bdd675dc94f659f298b832f20f7d4e752b7b5 100644 (file)
@@ -13,7 +13,7 @@
  *
  * Copyright (c) 2001-2003, PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/backend/postmaster/pgstat.c,v 1.46 2003/11/07 21:55:50 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/postmaster/pgstat.c,v 1.47 2003/11/15 17:24:07 tgl Exp $
  * ----------
  */
 #include "postgres.h"
@@ -203,6 +203,14 @@ pgstat_init(void)
        goto startup_failed;
    }
 
+   /*
+    * On some platforms, getaddrinfo_all() may return multiple addresses
+    * only one of which will actually work (eg, both IPv6 and IPv4 addresses
+    * when kernel will reject IPv6).  Worse, the failure may occur at the
+    * bind() or perhaps even connect() stage.  So we must loop through the
+    * results till we find a working combination.  We will generate LOG
+    * messages, but no error, for bogus combinations.
+    */
    for (addr = addrs; addr; addr = addr->ai_next)
    {
 #ifdef HAVE_UNIX_SOCKETS
@@ -210,53 +218,68 @@ pgstat_init(void)
        if (addr->ai_family == AF_UNIX)
            continue;
 #endif
-       if ((pgStatSock = socket(addr->ai_family, SOCK_DGRAM, 0)) >= 0)
-           break;
-   }
+       /*
+        * Create the socket.
+        */
+       if ((pgStatSock = socket(addr->ai_family, SOCK_DGRAM, 0)) < 0)
+       {
+           ereport(LOG,
+                   (errcode_for_socket_access(),
+                    errmsg("could not create socket for statistics collector: %m")));
+           continue;
+       }
 
-   if (!addr || pgStatSock < 0)
-   {
-       ereport(LOG,
-               (errcode_for_socket_access(),
-                errmsg("could not create socket for statistics collector: %m")));
-       goto startup_failed;
-   }
+       /*
+        * Bind it to a kernel assigned port on localhost and get the assigned
+        * port via getsockname().
+        */
+       if (bind(pgStatSock, addr->ai_addr, addr->ai_addrlen) < 0)
+       {
+           ereport(LOG,
+                   (errcode_for_socket_access(),
+                    errmsg("could not bind socket for statistics collector: %m")));
+           closesocket(pgStatSock);
+           pgStatSock = -1;
+           continue;
+       }
 
-   /*
-    * Bind it to a kernel assigned port on localhost and get the assigned
-    * port via getsockname().
-    */
-   if (bind(pgStatSock, addr->ai_addr, addr->ai_addrlen) < 0)
-   {
-       ereport(LOG,
-               (errcode_for_socket_access(),
-                errmsg("could not bind socket for statistics collector: %m")));
-       goto startup_failed;
-   }
+       alen = sizeof(pgStatAddr);
+       if (getsockname(pgStatSock, (struct sockaddr *) &pgStatAddr, &alen) < 0)
+       {
+           ereport(LOG,
+                   (errcode_for_socket_access(),
+                    errmsg("could not get address of socket for statistics collector: %m")));
+           closesocket(pgStatSock);
+           pgStatSock = -1;
+           continue;
+       }
 
-   freeaddrinfo_all(hints.ai_family, addrs);
-   addrs = NULL;
+       /*
+        * Connect the socket to its own address.  This saves a few cycles by
+        * not having to respecify the target address on every send. This also
+        * provides a kernel-level check that only packets from this same
+        * address will be received.
+        */
+       if (connect(pgStatSock, (struct sockaddr *) &pgStatAddr, alen) < 0)
+       {
+           ereport(LOG,
+                   (errcode_for_socket_access(),
+                    errmsg("could not connect socket for statistics collector: %m")));
+           closesocket(pgStatSock);
+           pgStatSock = -1;
+           continue;
+       }
 
-   alen = sizeof(pgStatAddr);
-   if (getsockname(pgStatSock, (struct sockaddr *) & pgStatAddr, &alen) < 0)
-   {
-       ereport(LOG,
-               (errcode_for_socket_access(),
-         errmsg("could not get address of socket for statistics collector: %m")));
-       goto startup_failed;
+       /* If we get here, we have a working socket */
+       break;
    }
 
-   /*
-    * Connect the socket to its own address.  This saves a few cycles by
-    * not having to respecify the target address on every send. This also
-    * provides a kernel-level check that only packets from this same
-    * address will be received.
-    */
-   if (connect(pgStatSock, (struct sockaddr *) & pgStatAddr, alen) < 0)
+   /* Did we find a working address? */
+   if (!addr || pgStatSock < 0)
    {
        ereport(LOG,
                (errcode_for_socket_access(),
-                errmsg("could not connect socket for statistics collector: %m")));
+                errmsg("disabling statistics collector for lack of working socket")));
        goto startup_failed;
    }
 
@@ -285,6 +308,8 @@ pgstat_init(void)
        goto startup_failed;
    }
 
+   freeaddrinfo_all(hints.ai_family, addrs);
+
    return;
 
 startup_failed: