]> granicus.if.org Git - postgresql/commitdiff
Avoid depending on non-POSIX behavior of fcntl(2).
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 21 Apr 2017 19:55:56 +0000 (15:55 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 21 Apr 2017 19:55:56 +0000 (15:55 -0400)
The POSIX standard does not say that the success return value for
fcntl(F_SETFD) and fcntl(F_SETFL) is zero; it says only that it's not -1.
We had several calls that were making the stronger assumption.  Adjust
them to test specifically for -1 for strict spec compliance.

The standard further leaves open the possibility that the O_NONBLOCK
flag bit is not the only active one in F_SETFL's argument.  Formally,
therefore, one ought to get the current flags with F_GETFL and store
them back with only the O_NONBLOCK bit changed when trying to change
the nonblock state.  In port/noblock.c, we were doing the full pushup
in pg_set_block but not in pg_set_noblock, which is just weird.  Make
both of them do it properly, since they have little business making
any assumptions about the socket they're handed.  The other places
where we're issuing F_SETFL are working with FDs we just got from
pipe(2), so it's reasonable to assume the FDs' properties are all
default, so I didn't bother adding F_GETFL steps there.

Also, while pg_set_block deserves some points for trying to do things
right, somebody had decided that it'd be even better to cast fcntl's
third argument to "long".  Which is completely loony, because POSIX
clearly says the third argument for an F_SETFL call is "int".

Given the lack of field complaints, these missteps apparently are not
of significance on any common platforms.  But they're still wrong,
so back-patch to all supported branches.

Discussion: https://postgr.es/m/30882.1492800880@sss.pgh.pa.us

contrib/pg_test_fsync/pg_test_fsync.c
src/backend/port/unix_latch.c
src/backend/postmaster/postmaster.c
src/port/noblock.c

index 37298be56e623c0b531b9fffd23a38de58d064c2..dcf1593af397db9af8b9099fe08f0d62af561c58 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <sys/stat.h>
 #include <sys/time.h>
+#include <fcntl.h>
 #include <time.h>
 #include <unistd.h>
 #include <signal.h>
index abfc2455dbe30eff6fa23e9bb10941bad65cdcee..583be6c060378b74c21b707e10e941cef2c944bf 100644 (file)
@@ -89,9 +89,9 @@ InitializeLatchSupport(void)
         */
        if (pipe(pipefd) < 0)
                elog(FATAL, "pipe() failed: %m");
-       if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) < 0)
+       if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) == -1)
                elog(FATAL, "fcntl() failed on read-end of self-pipe: %m");
-       if (fcntl(pipefd[1], F_SETFL, O_NONBLOCK) < 0)
+       if (fcntl(pipefd[1], F_SETFL, O_NONBLOCK) == -1)
                elog(FATAL, "fcntl() failed on write-end of self-pipe: %m");
 
        selfpipe_readfd = pipefd[0];
index 651e9114f11623ccc30a43f75538c768c1703f76..1b11687785269d97428f2d4e7e7cf5ea49f11596 100644 (file)
@@ -6244,7 +6244,7 @@ InitPostmasterDeathWatchHandle(void)
         * write fd. That is taken care of in ClosePostmasterPorts().
         */
        Assert(MyProcPid == PostmasterPid);
-       if (pipe(postmaster_alive_fds))
+       if (pipe(postmaster_alive_fds) < 0)
                ereport(FATAL,
                                (errcode_for_file_access(),
                                 errmsg_internal("could not create pipe to monitor postmaster death: %m")));
@@ -6253,7 +6253,7 @@ InitPostmasterDeathWatchHandle(void)
         * Set O_NONBLOCK to allow testing for the fd's presence with a read()
         * call.
         */
-       if (fcntl(postmaster_alive_fds[POSTMASTER_FD_WATCH], F_SETFL, O_NONBLOCK))
+       if (fcntl(postmaster_alive_fds[POSTMASTER_FD_WATCH], F_SETFL, O_NONBLOCK) == -1)
                ereport(FATAL,
                                (errcode_for_socket_access(),
                                 errmsg_internal("could not set postmaster death monitoring pipe to nonblocking mode: %m")));
index 1da0339ddb6df2f94ef205fc38ee7da10f840cfb..86ba4401dbb6fd25662a282f6141a8ad58d13c04 100644 (file)
@@ -1,7 +1,7 @@
 /*-------------------------------------------------------------------------
  *
  * noblock.c
- *       set a file descriptor as non-blocking
+ *       set a file descriptor as blocking or non-blocking
  *
  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
 #include <fcntl.h>
 
 
+/*
+ * Put socket into nonblock mode.
+ * Returns true on success, false on failure.
+ */
 bool
 pg_set_noblock(pgsocket sock)
 {
 #if !defined(WIN32)
-       return (fcntl(sock, F_SETFL, O_NONBLOCK) != -1);
+       int                     flags;
+
+       flags = fcntl(sock, F_GETFL);
+       if (flags < 0)
+               return false;
+       if (fcntl(sock, F_SETFL, (flags | O_NONBLOCK)) == -1)
+               return false;
+       return true;
 #else
        unsigned long ioctlsocket_ret = 1;
 
@@ -30,7 +41,10 @@ pg_set_noblock(pgsocket sock)
 #endif
 }
 
-
+/*
+ * Put socket into blocking mode.
+ * Returns true on success, false on failure.
+ */
 bool
 pg_set_block(pgsocket sock)
 {
@@ -38,7 +52,9 @@ pg_set_block(pgsocket sock)
        int                     flags;
 
        flags = fcntl(sock, F_GETFL);
-       if (flags < 0 || fcntl(sock, F_SETFL, (long) (flags & ~O_NONBLOCK)))
+       if (flags < 0)
+               return false;
+       if (fcntl(sock, F_SETFL, (flags & ~O_NONBLOCK)) == -1)
                return false;
        return true;
 #else