]> granicus.if.org Git - postgresql/blobdiff - src/backend/libpq/pqcomm.c
Post-PG 10 beta1 pgindent run
[postgresql] / src / backend / libpq / pqcomm.c
index 34efac48651538abe8a6bf5a4b6a4cc8a2179ba1..d1cc38beb2b25d6e38417a30e0651db7673c2c34 100644 (file)
@@ -27,7 +27,7 @@
  * the backend's "backend/libpq" is quite separate from "interfaces/libpq".
  * All that remains is similarities of names to trap the unwary...
  *
- * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *     src/backend/libpq/pqcomm.c
 #ifdef HAVE_UTIME_H
 #include <utime.h>
 #endif
-#ifdef WIN32_ONLY_COMPILER             /* mstcpip.h is missing on mingw */
+#ifdef _MSC_VER                                        /* mstcpip.h is missing on mingw */
 #include <mstcpip.h>
 #endif
 
-#include "libpq/ip.h"
+#include "common/ip.h"
 #include "libpq/libpq.h"
 #include "miscadmin.h"
 #include "storage/ipc.h"
@@ -145,7 +145,6 @@ static void socket_startcopyout(void);
 static void socket_endcopyout(bool errorAbort);
 static int     internal_putbytes(const char *s, size_t len);
 static int     internal_flush(void);
-static void socket_set_nonblocking(bool nonblocking);
 
 #ifdef HAVE_UNIX_SOCKETS
 static int     Lock_AF_UNIX(char *unixSocketDir, char *unixSocketPath);
@@ -165,6 +164,7 @@ static PQcommMethods PqCommSocketMethods = {
 
 PQcommMethods *PqCommMethods = &PqCommSocketMethods;
 
+WaitEventSet *FeBeWaitSet;
 
 
 /* --------------------------------
@@ -174,12 +174,15 @@ PQcommMethods *PqCommMethods = &PqCommSocketMethods;
 void
 pq_init(void)
 {
+       /* initialize state variables */
        PqSendBufferSize = PQ_SEND_BUFFER_SIZE;
        PqSendBuffer = MemoryContextAlloc(TopMemoryContext, PqSendBufferSize);
        PqSendPointer = PqSendStart = PqRecvPointer = PqRecvLength = 0;
        PqCommBusy = false;
        PqCommReadingMsg = false;
        DoingCopyOut = false;
+
+       /* set up process-exit hook to close the socket */
        on_proc_exit(socket_close, 0);
 
        /*
@@ -198,6 +201,11 @@ pq_init(void)
                                (errmsg("could not set socket to nonblocking mode: %m")));
 #endif
 
+       FeBeWaitSet = CreateWaitEventSet(TopMemoryContext, 3);
+       AddWaitEventToSet(FeBeWaitSet, WL_SOCKET_WRITEABLE, MyProcPort->sock,
+                                         NULL, NULL);
+       AddWaitEventToSet(FeBeWaitSet, WL_LATCH_SET, -1, MyLatch, NULL);
+       AddWaitEventToSet(FeBeWaitSet, WL_POSTMASTER_DEATH, -1, NULL, NULL);
 }
 
 /* --------------------------------
@@ -220,32 +228,45 @@ socket_comm_reset(void)
 /* --------------------------------
  *             socket_close - shutdown libpq at backend exit
  *
- * Note: in a standalone backend MyProcPort will be null,
- * don't crash during exit...
+ * This is the one pg_on_exit_callback in place during BackendInitialize().
+ * That function's unusual signal handling constrains that this callback be
+ * safe to run at any instant.
  * --------------------------------
  */
 static void
 socket_close(int code, Datum arg)
 {
+       /* Nothing to do in a standalone backend, where MyProcPort is NULL. */
        if (MyProcPort != NULL)
        {
 #if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 #ifdef ENABLE_GSS
                OM_uint32       min_s;
 
-               /* Shutdown GSSAPI layer */
+               /*
+                * Shutdown GSSAPI layer.  This section does nothing when interrupting
+                * BackendInitialize(), because pg_GSS_recvauth() makes first use of
+                * "ctx" and "cred".
+                */
                if (MyProcPort->gss->ctx != GSS_C_NO_CONTEXT)
                        gss_delete_sec_context(&min_s, &MyProcPort->gss->ctx, NULL);
 
                if (MyProcPort->gss->cred != GSS_C_NO_CREDENTIAL)
                        gss_release_cred(&min_s, &MyProcPort->gss->cred);
 #endif   /* ENABLE_GSS */
-               /* GSS and SSPI share the port->gss struct */
 
+               /*
+                * GSS and SSPI share the port->gss struct.  Since nowhere else does a
+                * postmaster child free this, doing so is safe when interrupting
+                * BackendInitialize().
+                */
                free(MyProcPort->gss);
 #endif   /* ENABLE_GSS || ENABLE_SSPI */
 
-               /* Cleanly shut down SSL layer */
+               /*
+                * Cleanly shut down SSL layer.  Nowhere else does a postmaster child
+                * call this, so this is safe when interrupting BackendInitialize().
+                */
                secure_close(MyProcPort);
 
                /*
@@ -272,28 +293,6 @@ socket_close(int code, Datum arg)
  */
 
 
-/* StreamDoUnlink()
- * Shutdown routine for backend connection
- * If any Unix sockets are used for communication, explicitly close them.
- */
-#ifdef HAVE_UNIX_SOCKETS
-static void
-StreamDoUnlink(int code, Datum arg)
-{
-       ListCell   *l;
-
-       /* Loop through all created sockets... */
-       foreach(l, sock_paths)
-       {
-               char       *sock_path = (char *) lfirst(l);
-
-               unlink(sock_path);
-       }
-       /* Since we're about to exit, no need to reclaim storage */
-       sock_paths = NIL;
-}
-#endif   /* HAVE_UNIX_SOCKETS */
-
 /*
  * StreamServerPort -- open a "listening" port to accept connections.
  *
@@ -320,6 +319,8 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
        char            portNumberStr[32];
        const char *familyDesc;
        char            familyDescBuf[64];
+       const char *addrDesc;
+       char            addrBuf[NI_MAXHOST];
        char       *service;
        struct addrinfo *addrs = NULL,
                           *addr;
@@ -408,7 +409,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
                        break;
                }
 
-               /* set up family name for possible error messages */
+               /* set up address family name for log messages */
                switch (addr->ai_family)
                {
                        case AF_INET:
@@ -432,13 +433,28 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
                                break;
                }
 
+               /* set up text form of address for log messages */
+#ifdef HAVE_UNIX_SOCKETS
+               if (addr->ai_family == AF_UNIX)
+                       addrDesc = unixSocketPath;
+               else
+#endif
+               {
+                       pg_getnameinfo_all((const struct sockaddr_storage *) addr->ai_addr,
+                                                          addr->ai_addrlen,
+                                                          addrBuf, sizeof(addrBuf),
+                                                          NULL, 0,
+                                                          NI_NUMERICHOST);
+                       addrDesc = addrBuf;
+               }
+
                if ((fd = socket(addr->ai_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET)
                {
                        ereport(LOG,
                                        (errcode_for_socket_access(),
-                       /* translator: %s is IPv4, IPv6, or Unix */
-                                        errmsg("could not create %s socket: %m",
-                                                       familyDesc)));
+                       /* translator: first %s is IPv4, IPv6, or Unix */
+                                 errmsg("could not create %s socket for address \"%s\": %m",
+                                                familyDesc, addrDesc)));
                        continue;
                }
 
@@ -462,7 +478,9 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
                        {
                                ereport(LOG,
                                                (errcode_for_socket_access(),
-                                                errmsg("setsockopt(SO_REUSEADDR) failed: %m")));
+                               /* translator: first %s is IPv4, IPv6, or Unix */
+                                                errmsg("setsockopt(SO_REUSEADDR) failed for %s address \"%s\": %m",
+                                                               familyDesc, addrDesc)));
                                closesocket(fd);
                                continue;
                        }
@@ -477,7 +495,9 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
                        {
                                ereport(LOG,
                                                (errcode_for_socket_access(),
-                                                errmsg("setsockopt(IPV6_V6ONLY) failed: %m")));
+                               /* translator: first %s is IPv4, IPv6, or Unix */
+                                                errmsg("setsockopt(IPV6_V6ONLY) failed for %s address \"%s\": %m",
+                                                               familyDesc, addrDesc)));
                                closesocket(fd);
                                continue;
                        }
@@ -495,9 +515,9 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
                {
                        ereport(LOG,
                                        (errcode_for_socket_access(),
-                       /* translator: %s is IPv4, IPv6, or Unix */
-                                        errmsg("could not bind %s socket: %m",
-                                                       familyDesc),
+                       /* translator: first %s is IPv4, IPv6, or Unix */
+                                        errmsg("could not bind %s address \"%s\": %m",
+                                                       familyDesc, addrDesc),
                                         (IS_AF_UNIX(addr->ai_family)) ?
                                  errhint("Is another postmaster already running on port %d?"
                                                  " If not, remove socket file \"%s\" and retry.",
@@ -534,12 +554,25 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
                {
                        ereport(LOG,
                                        (errcode_for_socket_access(),
-                       /* translator: %s is IPv4, IPv6, or Unix */
-                                        errmsg("could not listen on %s socket: %m",
-                                                       familyDesc)));
+                       /* translator: first %s is IPv4, IPv6, or Unix */
+                                        errmsg("could not listen on %s address \"%s\": %m",
+                                                       familyDesc, addrDesc)));
                        closesocket(fd);
                        continue;
                }
+
+#ifdef HAVE_UNIX_SOCKETS
+               if (addr->ai_family == AF_UNIX)
+                       ereport(LOG,
+                                       (errmsg("listening on Unix socket \"%s\"",
+                                                       addrDesc)));
+               else
+#endif
+                       ereport(LOG,
+                       /* translator: first %s is IPv4 or IPv6 */
+                                       (errmsg("listening on %s address \"%s\", port %d",
+                                                       familyDesc, addrDesc, (int) portNumber)));
+
                ListenSocket[listen_index] = fd;
                added++;
        }
@@ -575,16 +608,11 @@ Lock_AF_UNIX(char *unixSocketDir, char *unixSocketPath)
         * Once we have the interlock, we can safely delete any pre-existing
         * socket file to avoid failure at bind() time.
         */
-       unlink(unixSocketPath);
+       (void) unlink(unixSocketPath);
 
        /*
-        * Arrange to unlink the socket file(s) at proc_exit.  If this is the
-        * first one, set up the on_proc_exit function to do it; then add this
-        * socket file to the list of files to unlink.
+        * Remember socket file pathnames for later maintenance.
         */
-       if (sock_paths == NIL)
-               on_proc_exit(StreamDoUnlink, 0);
-
        sock_paths = lappend(sock_paths, pstrdup(unixSocketPath));
 
        return STATUS_OK;
@@ -689,16 +717,6 @@ StreamConnection(pgsocket server_fd, Port *port)
                return STATUS_ERROR;
        }
 
-#ifdef SCO_ACCEPT_BUG
-
-       /*
-        * UnixWare 7+ and OpenServer 5.0.4 are known to have this bug, but it
-        * shouldn't hurt to catch it for all versions of those platforms.
-        */
-       if (port->raddr.addr.ss_family == 0)
-               port->raddr.addr.ss_family = AF_UNIX;
-#endif
-
        /* fill in the server (local) address */
        port->laddr.salen = sizeof(port->laddr.addr);
        if (getsockname(port->sock,
@@ -713,6 +731,11 @@ StreamConnection(pgsocket server_fd, Port *port)
        if (!IS_AF_UNIX(port->laddr.addr.ss_family))
        {
                int                     on;
+#ifdef WIN32
+               int                     oldopt;
+               int                     optlen;
+               int                     newopt;
+#endif
 
 #ifdef TCP_NODELAY
                on = 1;
@@ -734,16 +757,43 @@ StreamConnection(pgsocket server_fd, Port *port)
 #ifdef WIN32
 
                /*
-                * This is a Win32 socket optimization.  The ideal size is 32k.
-                * http://support.microsoft.com/kb/823764/EN-US/
+                * This is a Win32 socket optimization.  The OS send buffer should be
+                * large enough to send the whole Postgres send buffer in one go, or
+                * performance suffers.  The Postgres send buffer can be enlarged if a
+                * very large message needs to be sent, but we won't attempt to
+                * enlarge the OS buffer if that happens, so somewhat arbitrarily
+                * ensure that the OS buffer is at least PQ_SEND_BUFFER_SIZE * 4.
+                * (That's 32kB with the current default).
+                *
+                * The default OS buffer size used to be 8kB in earlier Windows
+                * versions, but was raised to 64kB in Windows 2012.  So it shouldn't
+                * be necessary to change it in later versions anymore.  Changing it
+                * unnecessarily can even reduce performance, because setting
+                * SO_SNDBUF in the application disables the "dynamic send buffering"
+                * feature that was introduced in Windows 7.  So before fiddling with
+                * SO_SNDBUF, check if the current buffer size is already large enough
+                * and only increase it if necessary.
+                *
+                * See https://support.microsoft.com/kb/823764/EN-US/ and
+                * https://msdn.microsoft.com/en-us/library/bb736549%28v=vs.85%29.aspx
                 */
-               on = PQ_SEND_BUFFER_SIZE * 4;
-               if (setsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &on,
-                                          sizeof(on)) < 0)
+               optlen = sizeof(oldopt);
+               if (getsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &oldopt,
+                                          &optlen) < 0)
                {
-                       elog(LOG, "setsockopt(SO_SNDBUF) failed: %m");
+                       elog(LOG, "getsockopt(SO_SNDBUF) failed: %m");
                        return STATUS_ERROR;
                }
+               newopt = PQ_SEND_BUFFER_SIZE * 4;
+               if (oldopt < newopt)
+               {
+                       if (setsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &newopt,
+                                                  sizeof(newopt)) < 0)
+                       {
+                               elog(LOG, "setsockopt(SO_SNDBUF) failed: %m");
+                               return STATUS_ERROR;
+                       }
+               }
 #endif
 
                /*
@@ -813,6 +863,26 @@ TouchSocketFiles(void)
        }
 }
 
+/*
+ * RemoveSocketFiles -- unlink socket files at postmaster shutdown
+ */
+void
+RemoveSocketFiles(void)
+{
+       ListCell   *l;
+
+       /* Loop through all created sockets... */
+       foreach(l, sock_paths)
+       {
+               char       *sock_path = (char *) lfirst(l);
+
+               /* Ignore any error. */
+               (void) unlink(sock_path);
+       }
+       /* Since we're about to exit, no need to reclaim storage */
+       sock_paths = NIL;
+}
+
 
 /* --------------------------------
  * Low-level I/O routines begin here.
@@ -1112,7 +1182,7 @@ pq_getstring(StringInfo s)
 
 
 /* --------------------------------
- *             pq_startmsgread - begin reading a message from the client.
+ *             pq_startmsgread - begin reading a message from the client.
  *
  *             This must be called before any of the pq_get* functions.
  * --------------------------------
@@ -1127,7 +1197,7 @@ pq_startmsgread(void)
        if (PqCommReadingMsg)
                ereport(FATAL,
                                (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                                errmsg("terminating connection because protocol sync was lost")));
+                                errmsg("terminating connection because protocol synchronization was lost")));
 
        PqCommReadingMsg = true;
 }
@@ -1561,7 +1631,7 @@ socket_endcopyout(bool errorAbort)
 /*
  * On Windows, we need to set both idle and interval at the same time.
  * We also cannot reset them to the default (setting to zero will
- * actually set them to zero, not default), therefor we fallback to
+ * actually set them to zero, not default), therefore we fallback to
  * the out-of-the-box default instead.
  */
 #if defined(WIN32) && defined(SIO_KEEPALIVE_VALS)