diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 950bae25128726ea0ed3baae38501edbbb59ab46..2084404f0505ade65e80367506bc7542cf4ab616 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.241 2004/02/17 07:36:47 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.242 2004/02/23 20:45:58 tgl Exp $
 -->
 
 <Chapter Id="runtime">
@@ -932,19 +932,14 @@ SET ENABLE_SEQSCAN TO OFF;
       <listitem>
        <para>
         Sets the maximum number of simultaneously open files allowed to each
-        server subprocess. The default is 1000. The limit actually used
-        by the code is the smaller of this setting and the result of
-        <literal>sysconf(_SC_OPEN_MAX)</literal>. Therefore, on systems
-        where <function>sysconf</> returns a reasonable limit, you don't
-        need to worry about this setting. But on some platforms
-        (notably, most BSD systems), <function>sysconf</> returns a
-        value that is much larger than the system can really support
-        when a large number of processes all try to open that many
-        files. If you find yourself seeing <quote>Too many open files</>
-        failures, try reducing this setting. This option can only be set
-        at server start or in the <filename>postgresql.conf</filename>
-        configuration file; if changed in the configuration file, it
-        only affects subsequently-started server subprocesses.
+        server subprocess. The default is 1000. If the kernel is enforcing
+        a safe per-process limit, you don't need to worry about this setting.
+        But on some platforms (notably, most BSD systems), the kernel will
+        allow individual processes to open many more files than the system
+        can really support when a large number of processes all try to open
+        that many files. If you find yourself seeing <quote>Too many open
+        files</> failures, try reducing this setting.
+        This option can only be set at server start.
        </para>
       </listitem>
      </varlistentry>
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index ab9af54bac31eca339f63b5317ab893242286563..eb549f6b4a47d9f4efebd3a8b1e196e7be23fe14 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.367 2004/02/17 03:54:56 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.368 2004/02/23 20:45:59 tgl Exp $
  *
  * NOTES
  *
@@ -839,6 +839,12 @@ PostmasterMain(int argc, char *argv[])
 	 */
 	reset_shared(PostPortNumber);
 
+	/*
+	 * Estimate number of openable files.  This must happen after setting up
+	 * semaphores, because on some platforms semaphores count as open files.
+	 */
+	set_max_safe_fds();
+
 	/*
 	 * Initialize the list of active backends.
 	 */
@@ -848,13 +854,10 @@ PostmasterMain(int argc, char *argv[])
 	/*
 	 * Initialize the child pid/HANDLE arrays
 	 */
-	/* FIXME: [fork/exec] Ideally, we would resize these arrays with changes
-	 *  in MaxBackends, but this'll do as a first order solution.
-	 */
 	win32_childPIDArray = (pid_t*)malloc(NUM_BACKENDARRAY_ELEMS*sizeof(pid_t));
 	win32_childHNDArray = (HANDLE*)malloc(NUM_BACKENDARRAY_ELEMS*sizeof(HANDLE));
 	if (!win32_childPIDArray || !win32_childHNDArray)
-		ereport(LOG,
+		ereport(FATAL,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of memory")));
 #endif
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index df7d58d794051d7524250fc0f99f3fa13424d3e4..2446f97b653f9ed9517e3eb2ef00be59bf71d1ce 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/file/fd.c,v 1.106 2004/01/26 22:35:32 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/file/fd.c,v 1.107 2004/02/23 20:45:59 tgl Exp $
  *
  * NOTES:
  *
@@ -54,41 +54,50 @@
 
 
 /*
- * Problem: Postgres does a system(ld...) to do dynamic loading.
- * This will open several extra files in addition to those used by
- * Postgres.  We need to guarantee that there are file descriptors free
- * for ld to use.
+ * We must leave some file descriptors free for system(), the dynamic loader,
+ * and other code that tries to open files without consulting fd.c.  This
+ * is the number left free.  (While we can be pretty sure we won't get
+ * EMFILE, there's never any guarantee that we won't get ENFILE due to
+ * other processes chewing up FDs.  So it's a bad idea to try to open files
+ * without consulting fd.c.  Nonetheless we cannot control all code.)
  *
- * The current solution is to limit the number of file descriptors
- * that this code will allocate at one time: it leaves RESERVE_FOR_LD free.
- *
- * (Even though most dynamic loaders now use dlopen(3) or the
- * equivalent, the OS must still open several files to perform the
- * dynamic loading.  And stdin/stdout/stderr count too.  Keep this here.)
+ * Because this is just a fixed setting, we are effectively assuming that
+ * no such code will leave FDs open over the long term; otherwise the slop
+ * is likely to be insufficient.  Note in particular that we expect that
+ * loading a shared library does not result in any permanent increase in
+ * the number of open files.  (This appears to be true on most if not
+ * all platforms as of Feb 2004.)
  */
-#ifndef RESERVE_FOR_LD
-#define RESERVE_FOR_LD	10
-#endif
+#define NUM_RESERVED_FDS		10
 
 /*
- * We need to ensure that we have at least some file descriptors
- * available to postgreSQL after we've reserved the ones for LD,
- * so we set that value here.
- *
- * I think 10 is an appropriate value so that's what it'll be
- * for now.
+ * If we have fewer than this many usable FDs after allowing for the reserved
+ * ones, choke.
  */
-#ifndef FD_MINFREE
-#define FD_MINFREE 10
-#endif
+#define FD_MINFREE				10
+
 
 /*
- * A number of platforms return values for sysconf(_SC_OPEN_MAX) that are
- * far beyond what they can really support.  This GUC parameter limits what
- * we will believe.
+ * A number of platforms allow individual processes to open many more files
+ * than they can really support when *many* processes do the same thing.
+ * This GUC parameter lets the DBA limit max_safe_fds to something less than
+ * what the postmaster's initial probe suggests will work.
  */
 int			max_files_per_process = 1000;
 
+/*
+ * Maximum number of file descriptors to open for either VFD entries or
+ * AllocateFile files.  This is initialized to a conservative value, and
+ * remains that way indefinitely in bootstrap or standalone-backend cases.
+ * In normal postmaster operation, the postmaster calls set_max_safe_fds()
+ * late in initialization to update the value, and that value is then
+ * inherited by forked subprocesses.
+ *
+ * Note: the value of max_files_per_process is taken into account while
+ * setting this variable, and so need not be tested separately.
+ */
+static int	max_safe_fds = 32;			/* default if not changed */
+
 
 /* Debugging.... */
 
@@ -199,7 +208,6 @@ static void FreeVfd(File file);
 static int	FileAccess(File file);
 static File fileNameOpenFile(FileName fileName, int fileFlags, int fileMode);
 static char *filepath(const char *filename);
-static long pg_nofile(void);
 static void AtProcExit_Files(int code, Datum arg);
 static void CleanupTempFiles(bool isProcExit);
 
@@ -236,6 +244,105 @@ pg_fdatasync(int fd)
 		return 0;
 }
 
+/*
+ * count_usable_fds --- count how many FDs the system will let us open,
+ *		and estimate how many are already open.
+ *
+ * We assume stdin (FD 0) is available for dup'ing
+ */
+static void
+count_usable_fds(int *usable_fds, int *already_open)
+{
+	int		   *fd;
+	int			size;
+	int			used = 0;
+	int			highestfd = 0;
+	int			j;
+
+	size = 1024;
+	fd = (int *) palloc(size * sizeof(int));
+
+	/* dup until failure ... */
+	for (;;)
+	{
+		int		thisfd;
+
+		thisfd = dup(0);
+		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);
+			break;
+		}
+
+		if (used >= size)
+		{
+			size *= 2;
+			fd = (int *) repalloc(fd, size * sizeof(int));
+		}
+		fd[used++] = thisfd;
+
+		if (highestfd < thisfd)
+			highestfd = thisfd;
+	}
+
+	/* release the files we opened */
+	for (j = 0; j < used; j++)
+		close(fd[j]);
+
+	pfree(fd);
+
+	/*
+	 * Return results.  usable_fds is just the number of successful dups.
+	 * We assume that the system limit is highestfd+1 (remember 0 is a legal
+	 * FD number) and so already_open is highestfd+1 - usable_fds.
+	 */
+	*usable_fds = used;
+	*already_open = highestfd+1 - used;
+}
+
+/*
+ * set_max_safe_fds
+ *		Determine number of filedescriptors that fd.c is allowed to use
+ */
+void
+set_max_safe_fds(void)
+{
+	int			usable_fds;
+	int			already_open;
+
+	/*
+	 * We want to set max_safe_fds to
+	 *			MIN(usable_fds, max_files_per_process - already_open)
+	 * less the slop factor for files that are opened without consulting
+	 * fd.c.  This ensures that we won't exceed either max_files_per_process
+	 * or the experimentally-determined EMFILE limit.
+	 */
+	count_usable_fds(&usable_fds, &already_open);
+
+	max_safe_fds = Min(usable_fds, max_files_per_process - already_open);
+
+	/*
+	 * Take off the FDs reserved for system() etc.
+	 */
+	max_safe_fds -= NUM_RESERVED_FDS;
+
+	/*
+	 * Make sure we still have enough to get by.
+	 */
+	if (max_safe_fds < FD_MINFREE)
+		ereport(FATAL,
+				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+				 errmsg("insufficient file descriptors available to start server process"),
+				 errdetail("System allows %d, we need at least %d.",
+						   max_safe_fds + NUM_RESERVED_FDS,
+						   FD_MINFREE + NUM_RESERVED_FDS)));
+
+	elog(DEBUG2, "max_safe_fds = %d, usable_fds = %d, already_open = %d",
+		 max_safe_fds, usable_fds, already_open);
+}
+
 /*
  * BasicOpenFile --- same as open(2) except can free other FDs if needed
  *
@@ -279,63 +386,6 @@ tryAgain:
 	return -1;					/* failure */
 }
 
-/*
- * pg_nofile: determine number of filedescriptors that fd.c is allowed to use
- */
-static long
-pg_nofile(void)
-{
-	static long no_files = 0;
-
-	/* need do this calculation only once */
-	if (no_files == 0)
-	{
-		/*
-		 * Ask the system what its files-per-process limit is.
-		 */
-#ifdef HAVE_SYSCONF
-		no_files = sysconf(_SC_OPEN_MAX);
-		if (no_files <= 0)
-		{
-#ifdef NOFILE
-			no_files = (long) NOFILE;
-#else
-			no_files = (long) max_files_per_process;
-#endif
-			elog(LOG, "sysconf(_SC_OPEN_MAX) failed; using %ld",
-				 no_files);
-		}
-#else							/* !HAVE_SYSCONF */
-#ifdef NOFILE
-		no_files = (long) NOFILE;
-#else
-		no_files = (long) max_files_per_process;
-#endif
-#endif   /* HAVE_SYSCONF */
-
-		/*
-		 * Some platforms return hopelessly optimistic values.	Apply a
-		 * configurable upper limit.
-		 */
-		if (no_files > (long) max_files_per_process)
-			no_files = (long) max_files_per_process;
-
-		/*
-		 * Make sure we have enough to get by after reserving some for LD.
-		 */
-		if ((no_files - RESERVE_FOR_LD) < FD_MINFREE)
-			ereport(FATAL,
-					(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
-					 errmsg("insufficient file descriptors available to start server process"),
-					 errdetail("System allows %ld, we need at least %d.",
-							   no_files, RESERVE_FOR_LD + FD_MINFREE)));
-
-		no_files -= RESERVE_FOR_LD;
-	}
-
-	return no_files;
-}
-
 #if defined(FDDEBUG)
 
 static void
@@ -439,7 +489,7 @@ LruInsert(File file)
 
 	if (FileIsNotOpen(file))
 	{
-		while (nfile + numAllocatedFiles >= pg_nofile())
+		while (nfile + numAllocatedFiles >= max_safe_fds)
 		{
 			if (!ReleaseLruFile())
 				break;
@@ -698,7 +748,7 @@ fileNameOpenFile(FileName fileName,
 	file = AllocateVfd();
 	vfdP = &VfdCache[file];
 
-	while (nfile + numAllocatedFiles >= pg_nofile())
+	while (nfile + numAllocatedFiles >= max_safe_fds)
 	{
 		if (!ReleaseLruFile())
 			break;
@@ -1042,7 +1092,14 @@ AllocateFile(char *name, char *mode)
 
 	DO_DB(elog(LOG, "AllocateFile: Allocated %d", numAllocatedFiles));
 
-	if (numAllocatedFiles >= MAX_ALLOCATED_FILES)
+	/*
+	 * The test against MAX_ALLOCATED_FILES prevents us from overflowing
+	 * allocatedFiles[]; the test against max_safe_fds prevents AllocateFile
+	 * from hogging every one of the available FDs, which'd lead to infinite
+	 * looping.
+	 */
+	if (numAllocatedFiles >= MAX_ALLOCATED_FILES ||
+		numAllocatedFiles >= max_safe_fds - 1)
 		elog(ERROR, "too many private FDs demanded");
 
 TryAgain:
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 1dbf330ef69e3ba4df2e7a1139b649c3a5659372..85931e3ca83c1e8c2601817cf5f923f2e2ec45fc 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.187 2004/02/17 03:54:57 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.188 2004/02/23 20:45:59 tgl Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -1102,7 +1102,7 @@ static struct config_int ConfigureNamesInt[] =
 	},
 
 	{
-		{"max_files_per_process", PGC_BACKEND, RESOURCES_KERNEL,
+		{"max_files_per_process", PGC_POSTMASTER, RESOURCES_KERNEL,
 			gettext_noop("Sets the maximum number of simultaneously open files for each server process."),
 			NULL
 		},
diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h
index 26f30ffd537c29dbf5cee1746ad10010fea01f65..feca2b92b197c79e38501c7d9b0c5653b7b9c531 100644
--- a/src/include/storage/fd.h
+++ b/src/include/storage/fd.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/fd.h,v 1.42 2004/01/26 22:35:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/fd.h,v 1.43 2004/02/23 20:45:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -71,6 +71,7 @@ extern int	FreeFile(FILE *);
 extern int	BasicOpenFile(FileName fileName, int fileFlags, int fileMode);
 
 /* Miscellaneous support routines */
+extern void set_max_safe_fds(void);
 extern void closeAllVfds(void);
 extern void AtEOXact_Files(void);
 extern void RemovePgTempFiles(void);