In count_usable_fds(), duplicate stderr not stdin.
authorTom Lane <[email protected]>
Thu, 2 Sep 2021 22:53:10 +0000 (18:53 -0400)
committerTom Lane <[email protected]>
Thu, 2 Sep 2021 22:53:10 +0000 (18:53 -0400)
We had a complaint that the postmaster fails to start if the invoking
program closes stdin.  That happens because count_usable_fds expects
to be able to dup(0), and if it can't, we conclude there are no free
FDs and go belly-up.  So far as I can find, though, there is no other
place in the server that touches stdin, and it's not unreasonable to
expect that a daemon wouldn't use that file.

As a simple improvement, let's dup FD 2 (stderr) instead.  Unlike stdin,
it *is* reasonable for us to expect that stderr be open; even if we are
configured not to touch it, common libraries such as libc might try to
write error messages there.

Per gripe from Mario Emmenlauer.  Given the lack of previous complaints,
I'm not excited about pushing this into stable branches, but it seems
OK to squeeze it into v14.

Discussion: https://postgr.es/m/48bafc63-c30f-3962-2ded-f2e985d93e86@emmenlauer.de

src/backend/storage/file/fd.c

index 433e2832a540a89e1e0cc328f50d19b01a67d6b1..f9cda6906d2ba381d3a17e972149418b718eac44 100644 (file)
@@ -934,7 +934,9 @@ InitTemporaryFileAccess(void)
  * of already_open will give the right answer.  In practice, max_to_probe
  * of a couple of dozen should be enough to ensure good results.
  *
- * We assume stdin (FD 0) is available for dup'ing
+ * We assume stderr (FD 2) is available for dup'ing.  While the calling
+ * script could theoretically close that, it would be a really bad idea,
+ * since then one risks loss of error messages from, e.g., libc.
  */
 static void
 count_usable_fds(int max_to_probe, int *usable_fds, int *already_open)
@@ -978,12 +980,12 @@ count_usable_fds(int max_to_probe, int *usable_fds, int *already_open)
                        break;
 #endif
 
-               thisfd = dup(0);
+               thisfd = dup(2);
                if (thisfd < 0)
                {
                        /* Expect EMFILE or ENFILE, else it's fishy */
                        if (errno != EMFILE && errno != ENFILE)
-                               elog(WARNING, "dup(0) failed after %d successes: %m", used);
+                               elog(WARNING, "duplicating stderr file descriptor failed after %d successes: %m", used);
                        break;
                }