From 0021ae06be3a74978976ca970bd01941dde70291 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 17 Nov 2004 00:14:14 +0000
Subject: [PATCH] Fix Win32 problems with signals and sockets, by making the
 forkexec code even uglier than it was already :-(.  Also, on Windows only,
 use temporary shared memory segments instead of ordinary files to pass over
 critical variable values from postmaster to child processes.  Magnus Hagander

---
 src/backend/main/main.c             |  15 +-
 src/backend/port/win32/signal.c     |  46 +-
 src/backend/postmaster/pgstat.c     |  35 +-
 src/backend/postmaster/postmaster.c | 751 ++++++++++++++++++++--------
 src/include/port/win32.h            |   7 +-
 5 files changed, 615 insertions(+), 239 deletions(-)

diff --git a/src/backend/main/main.c b/src/backend/main/main.c
index e63006c2615..315977a2b6a 100644
--- a/src/backend/main/main.c
+++ b/src/backend/main/main.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/main/main.c,v 1.92 2004/11/05 17:11:17 petere Exp $
+ *	  $PostgreSQL: pgsql/src/backend/main/main.c,v 1.93 2004/11/17 00:14:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -118,9 +118,6 @@ main(int argc, char *argv[])
 						 argv[0], err);
 			exit(1);
 		}
-
-		/* Start our win32 signal implementation */
-		pgwin32_signal_initialize();
 	}
 #endif
 
@@ -281,6 +278,16 @@ main(int argc, char *argv[])
 		exit(SubPostmasterMain(argc, argv));
 #endif
 
+#ifdef WIN32
+	/*
+	 * Start our win32 signal implementation
+	 *
+	 * SubPostmasterMain() will do this for itself, but the remaining
+	 * modes need it here
+	 */
+	pgwin32_signal_initialize();
+#endif
+
 	/*
 	 * If the first argument is "-boot", then invoke bootstrap mode. (This
 	 * path is taken only for a standalone bootstrap process.)
diff --git a/src/backend/port/win32/signal.c b/src/backend/port/win32/signal.c
index c99e170c38a..1efaface8d1 100644
--- a/src/backend/port/win32/signal.c
+++ b/src/backend/port/win32/signal.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/port/win32/signal.c,v 1.9 2004/11/09 13:01:25 petere Exp $
+ *	  $PostgreSQL: pgsql/src/backend/port/win32/signal.c,v 1.10 2004/11/17 00:14:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,6 +26,7 @@ static pqsigfunc pg_signal_defaults[PG_SIGNAL_COUNT];
 static int	pg_signal_mask;
 
 DLLIMPORT HANDLE pgwin32_signal_event;
+HANDLE pgwin32_initial_signal_pipe = INVALID_HANDLE_VALUE;
 
 
 /* Signal handling thread function */
@@ -154,6 +155,28 @@ pqsignal(int signum, pqsigfunc handler)
 	return prevfunc;
 }
 
+/* Create the signal listener pipe for specified pid */
+HANDLE
+pgwin32_create_signal_listener(pid_t pid)
+{
+	char		pipename[128];
+	HANDLE		pipe;
+
+	wsprintf(pipename, "\\\\.\\pipe\\pgsignal_%d", (int) pid);
+
+	pipe = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX,
+						   PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
+						   PIPE_UNLIMITED_INSTANCES, 16, 16, 1000, NULL);
+
+	if (pipe == INVALID_HANDLE_VALUE)
+		ereport(ERROR,
+				(errmsg("could not create signal listener pipe for pid %d: error code %d",
+						(int) pid, (int) GetLastError())));
+
+	return pipe;
+}
+
+
 /*
  * All functions below execute on the signal handler thread
  * and must be synchronized as such!
@@ -210,7 +233,7 @@ static DWORD WINAPI
 pg_signal_thread(LPVOID param)
 {
 	char		pipename[128];
-	HANDLE		pipe = INVALID_HANDLE_VALUE;
+	HANDLE      pipe = pgwin32_initial_signal_pipe;
 
 	wsprintf(pipename, "\\\\.\\pipe\\pgsignal_%d", GetCurrentProcessId());
 
@@ -219,14 +242,18 @@ pg_signal_thread(LPVOID param)
 		BOOL		fConnected;
 		HANDLE		hThread;
 
-		pipe = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX,
-				   PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
-						   PIPE_UNLIMITED_INSTANCES, 16, 16, 1000, NULL);
 		if (pipe == INVALID_HANDLE_VALUE)
 		{
-			write_stderr("could not create signal listener pipe: error code %d; retrying\n", (int) GetLastError());
-			SleepEx(500, FALSE);
-			continue;
+			pipe = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX,
+								   PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
+								   PIPE_UNLIMITED_INSTANCES, 16, 16, 1000, NULL);
+
+			if (pipe == INVALID_HANDLE_VALUE)
+			{
+				write_stderr("could not create signal listener pipe: error code %d; retrying\n", (int) GetLastError());
+				SleepEx(500, FALSE);
+				continue;
+			}
 		}
 
 		fConnected = ConnectNamedPipe(pipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
@@ -244,6 +271,9 @@ pg_signal_thread(LPVOID param)
 		else
 			/* Connection failed. Cleanup and try again */
 			CloseHandle(pipe);
+
+		/* Set up so we create a new pipe on next loop */
+		pipe = INVALID_HANDLE_VALUE;
 	}
 	return 0;
 }
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 20eee0a7cf6..8ec50623ca8 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -13,7 +13,7 @@
  *
  *	Copyright (c) 2001-2004, PostgreSQL Global Development Group
  *
- *	$PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.84 2004/10/28 01:38:41 neilc Exp $
+ *	$PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.85 2004/11/17 00:14:12 tgl Exp $
  * ----------
  */
 #include "postgres.h"
@@ -110,8 +110,9 @@ bool		pgstat_collect_blocklevel = false;
  * ----------
  */
 NON_EXEC_STATIC int pgStatSock = -1;
-static int	pgStatPipe[2];
+NON_EXEC_STATIC int pgStatPipe[2] = {-1,-1};
 static struct sockaddr_storage pgStatAddr;
+static pid_t pgStatCollectorPid = 0;
 
 static time_t last_pgstat_start_time;
 
@@ -492,10 +493,6 @@ pgstat_forkexec(STATS_PROCESS_TYPE procType)
 	/* postgres_exec_path is not passed by write_backend_variables */
 	av[ac++] = postgres_exec_path;
 
-	/* Pipe file ids (those not passed by write_backend_variables) */
-	snprintf(pgstatBuf[bufc++], 32, "%d", pgStatPipe[0]);
-	snprintf(pgstatBuf[bufc++], 32, "%d", pgStatPipe[1]);
-
 	/* Add to the arg list */
 	Assert(bufc <= lengthof(pgstatBuf));
 	for (i = 0; i < bufc; i++)
@@ -517,12 +514,10 @@ pgstat_forkexec(STATS_PROCESS_TYPE procType)
 static void
 pgstat_parseArgs(int argc, char *argv[])
 {
-	Assert(argc == 6);
+	Assert(argc == 4);
 
 	argc = 3;
 	StrNCpy(postgres_exec_path, argv[argc++], MAXPGPATH);
-	pgStatPipe[0] = atoi(argv[argc++]);
-	pgStatPipe[1] = atoi(argv[argc++]);
 }
 #endif   /* EXEC_BACKEND */
 
@@ -1385,12 +1380,13 @@ PgstatBufferMain(int argc, char *argv[])
 				(errcode_for_socket_access(),
 			 errmsg("could not create pipe for statistics buffer: %m")));
 
-#ifdef EXEC_BACKEND
 	/* child becomes collector process */
-	switch (pgstat_forkexec(STAT_PROC_COLLECTOR))
+#ifdef EXEC_BACKEND
+	pgStatCollectorPid = pgstat_forkexec(STAT_PROC_COLLECTOR);
 #else
-	switch (fork())
+	pgStatCollectorPid = fork();
 #endif
+	switch (pgStatCollectorPid)
 	{
 		case -1:
 			ereport(ERROR,
@@ -1445,7 +1441,12 @@ PgstatCollectorMain(int argc, char *argv[])
 	pqsignal(SIGHUP, SIG_IGN);
 	pqsignal(SIGINT, SIG_IGN);
 	pqsignal(SIGTERM, SIG_IGN);
+#ifndef WIN32
 	pqsignal(SIGQUIT, SIG_IGN);
+#else
+	/* kluge to allow buffer process to kill collector; FIXME */
+	pqsignal(SIGQUIT, pgstat_exit);
+#endif
 	pqsignal(SIGALRM, SIG_IGN);
 	pqsignal(SIGPIPE, SIG_IGN);
 	pqsignal(SIGUSR1, SIG_IGN);
@@ -1943,6 +1944,16 @@ pgstat_exit(SIGNAL_ARGS)
 	 * be cleaner to allow any pending messages to be sent, but that
 	 * creates a tradeoff against speed of exit.
 	 */
+
+	/*
+	 * If running in bufferer, kill our collector as well. On some broken
+	 * win32 systems, it does not shut down automatically because of issues
+	 * with socket inheritance.  XXX so why not fix the socket inheritance...
+	 */
+#ifdef WIN32
+	if (pgStatCollectorPid > 0)
+		kill(pgStatCollectorPid, SIGQUIT);
+#endif
 	exit(0);
 }
 
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 724717af8c3..a8e22cbd8ee 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.438 2004/11/14 19:35:30 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.439 2004/11/17 00:14:12 tgl Exp $
  *
  * NOTES
  *
@@ -120,6 +120,10 @@
 #include "bootstrap/bootstrap.h"
 #include "pgstat.h"
 
+#ifdef EXEC_BACKEND
+#include "storage/spin.h"
+#endif
+
 
 /*
  * List of active backends (or child processes anyway; we don't actually
@@ -273,7 +277,6 @@ static pid_t StartChildProcess(int xlop);
 #ifdef EXEC_BACKEND
 
 #ifdef WIN32
-static pid_t win32_forkexec(const char *path, char *argv[]);
 static void win32_AddChild(pid_t pid, HANDLE handle);
 static void win32_RemoveChild(pid_t pid);
 static pid_t win32_waitpid(int *exitstatus);
@@ -289,11 +292,67 @@ HANDLE		PostmasterHandle;
 static pid_t backend_forkexec(Port *port);
 static pid_t internal_forkexec(int argc, char *argv[], Port *port);
 
-static void read_backend_variables(char *filename, Port *port);
-static bool write_backend_variables(char *filename, Port *port);
+/* Type for a socket that can be inherited to a client process */
+#ifdef WIN32
+typedef struct
+{
+	SOCKET origsocket; /* Original socket value, or -1 if not a socket */
+	WSAPROTOCOL_INFO wsainfo;
+} InheritableSocket;
+#else
+typedef int InheritableSocket;
+#endif
+
+typedef struct LWLock LWLock;	/* ugly kluge */
+
+/*
+ * Structure contains all variables passed to exec:ed backends
+ */
+typedef struct
+{
+	Port port;
+	InheritableSocket portsocket;
+	char DataDir[MAXPGPATH];
+	int ListenSocket[MAXLISTEN];
+	long MyCancelKey;
+	unsigned long UsedShmemSegID;
+	void *UsedShmemSegAddr;
+	slock_t *ShmemLock;
+	slock_t *ShmemIndexLock;
+	VariableCache ShmemVariableCache;
+	void *ShmemIndexAlloc;
+	Backend *ShmemBackendArray;
+	LWLock *LWLockArray;
+	slock_t *ProcStructLock;
+	InheritableSocket pgStatSock;
+	InheritableSocket pgStatPipe0;
+	InheritableSocket pgStatPipe1;
+	pid_t PostmasterPid;
+#ifdef WIN32
+	HANDLE PostmasterHandle;
+	HANDLE initial_signal_pipe;
+	HANDLE syslogPipe[2];
+#else
+	int syslogPipe[2];
+#endif
+	char my_exec_path[MAXPGPATH];
+	char ExtraOptions[MAXPGPATH];
+	char lc_collate[MAXPGPATH];
+	char lc_ctype[MAXPGPATH];
+} BackendParameters;
+
+static void read_backend_variables(char *id, Port *port);
+static void restore_backend_variables(BackendParameters *param, Port *port);
+#ifndef WIN32
+static bool save_backend_variables(BackendParameters *param, Port *port);
+#else
+static bool save_backend_variables(BackendParameters *param, Port *port,
+								   HANDLE childProcess, pid_t childPid);
+#endif
 
 static void ShmemBackendArrayAdd(Backend *bn);
 static void ShmemBackendArrayRemove(pid_t pid);
+
 #endif   /* EXEC_BACKEND */
 
 #define StartupDataBase()		StartChildProcess(BS_XLOG_STARTUP)
@@ -336,6 +395,11 @@ PostmasterMain(int argc, char *argv[])
 		}
 	}
 
+#ifdef WIN32
+	/* Start our win32 signal implementation */
+	pgwin32_signal_initialize();
+#endif
+
 	/*
 	 * for security, no dir or file created can be group or other
 	 * accessible
@@ -756,7 +820,7 @@ PostmasterMain(int argc, char *argv[])
 						TRUE,
 						DUPLICATE_SAME_ACCESS) == 0)
 		ereport(FATAL,
-			(errmsg_internal("could not duplicate postmaster handle: %d",
+			(errmsg_internal("could not duplicate postmaster handle: error code %d",
 							 (int) GetLastError())));
 #endif
 
@@ -2799,14 +2863,70 @@ backend_forkexec(Port *port)
 	return internal_forkexec(ac, av, port);
 }
 
+#ifndef WIN32
+
+/*
+ * internal_forkexec non-win32 implementation
+ *
+ * - writes out backend variables to the parameter file
+ * - fork():s, and then exec():s the child process
+ */
 static pid_t
 internal_forkexec(int argc, char *argv[], Port *port)
 {
+	static unsigned long tmpBackendFileNum = 0;
 	pid_t		pid;
 	char		tmpfilename[MAXPGPATH];
+	BackendParameters param;
+	FILE       *fp;
+
+	if (!save_backend_variables(&param, port))
+		return -1;				/* log made by save_backend_variables */
+
+	/* Calculate name for temp file */
+	Assert(DataDir);
+	snprintf(tmpfilename, MAXPGPATH, "%s/%s/%s.backend_var.%d.%lu",
+			 DataDir, PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX,
+			 MyProcPid, ++tmpBackendFileNum);
 
-	if (!write_backend_variables(tmpfilename, port))
-		return -1;				/* log made by write_backend_variables */
+	/* Open file */
+	fp = AllocateFile(tmpfilename, PG_BINARY_W);
+	if (!fp)
+	{
+		/* As per OpenTemporaryFile... */
+		char		dirname[MAXPGPATH];
+
+		snprintf(dirname, MAXPGPATH, "%s/%s", DataDir, PG_TEMP_FILES_DIR);
+		mkdir(dirname, S_IRWXU);
+
+		fp = AllocateFile(tmpfilename, PG_BINARY_W);
+		if (!fp)
+		{
+			ereport(LOG,
+					(errcode_for_file_access(),
+					 errmsg("could not create file \"%s\": %m",
+							tmpfilename)));
+			return -1;
+		}
+	}
+
+	if (fwrite(&param, sizeof(param), 1, fp) != 1)
+	{
+		ereport(LOG,
+				(errcode_for_file_access(),
+				 errmsg("could not write to file \"%s\": %m", tmpfilename)));
+		FreeFile(fp);
+		return -1;
+	}
+
+	/* Release file */
+	if (FreeFile(fp))
+	{
+		ereport(LOG,
+				(errcode_for_file_access(),
+				 errmsg("could not write to file \"%s\": %m", tmpfilename)));
+		return -1;
+	}
 
 	/* Make sure caller set up argv properly */
 	Assert(argc >= 3);
@@ -2817,9 +2937,6 @@ internal_forkexec(int argc, char *argv[], Port *port)
 	/* Insert temp file name after -fork argument */
 	argv[2] = tmpfilename;
 
-#ifdef WIN32
-	pid = win32_forkexec(postgres_exec_path, argv);
-#else
 	/* Fire off execv in child */
 	if ((pid = fork()) == 0)
 	{
@@ -2832,12 +2949,188 @@ internal_forkexec(int argc, char *argv[], Port *port)
 			exit(1);
 		}
 	}
-#endif
 
 	return pid;					/* Parent returns pid, or -1 on fork
 								 * failure */
 }
 
+#else /* WIN32 */
+
+/*
+ * internal_forkexec win32 implementation
+ *
+ * - starts backend using CreateProcess(), in suspended state
+ * - writes out backend variables to the parameter file
+ *  - during this, duplicates handles and sockets required for
+ *    inheritance into the new process
+ * - resumes execution of the new process once the backend parameter
+ *   file is complete.
+ */
+static pid_t
+internal_forkexec(int argc, char *argv[], Port *port)
+{
+	STARTUPINFO si;
+	PROCESS_INFORMATION pi;
+	int			i;
+	int			j;
+	char		cmdLine[MAXPGPATH * 2];
+	HANDLE		childHandleCopy;
+	HANDLE		waiterThread;
+	HANDLE      paramHandle;
+	BackendParameters *param;
+	SECURITY_ATTRIBUTES sa;
+	char        paramHandleStr[32];
+
+	/* Make sure caller set up argv properly */
+	Assert(argc >= 3);
+	Assert(argv[argc] == NULL);
+	Assert(strncmp(argv[1], "-fork", 5) == 0);
+	Assert(argv[2] == NULL);
+
+	/* Set up shared memory for parameter passing */
+	ZeroMemory(&sa,sizeof(sa));
+	sa.nLength = sizeof(sa);
+	sa.bInheritHandle = TRUE;
+	paramHandle = CreateFileMapping(INVALID_HANDLE_VALUE,
+									&sa,
+									PAGE_READWRITE,
+									0,
+									sizeof(BackendParameters),
+									NULL);
+	if (paramHandle == INVALID_HANDLE_VALUE)
+	{
+		elog(LOG, "could not create backend parameter file mapping: error code %d",
+			 (int) GetLastError());
+		return -1;
+	}
+
+	param = MapViewOfFile(paramHandle, FILE_MAP_WRITE, 0, 0, sizeof(BackendParameters));
+	if (!param)
+	{
+		elog(LOG, "could not map backend parameter memory: error code %d",
+			 (int) GetLastError());
+		CloseHandle(paramHandle);
+		return -1;
+	}
+
+	/* Insert temp file name after -fork argument */
+	sprintf(paramHandleStr, "%lu", (DWORD)paramHandle);
+	argv[2] = paramHandleStr;
+
+	/* Format the cmd line */
+	cmdLine[sizeof(cmdLine) - 1] = '\0';
+	cmdLine[sizeof(cmdLine) - 2] = '\0';
+	snprintf(cmdLine, sizeof(cmdLine) - 1, "\"%s\"", postgres_exec_path);
+	i = 0;
+	while (argv[++i] != NULL)
+	{
+		j = strlen(cmdLine);
+		snprintf(cmdLine + j, sizeof(cmdLine) - 1 - j, " \"%s\"", argv[i]);
+	}
+	if (cmdLine[sizeof(cmdLine) - 2] != '\0')
+	{
+		elog(LOG, "subprocess command line too long");
+		return -1;
+	}
+
+	memset(&pi, 0, sizeof(pi));
+	memset(&si, 0, sizeof(si));
+	si.cb = sizeof(si);
+	/*
+	 * Create the subprocess in a suspended state. This will be resumed
+	 * later, once we have written out the parameter file.
+	 */
+	if (!CreateProcess(NULL, cmdLine, NULL, NULL, TRUE, CREATE_SUSPENDED,
+					   NULL, NULL, &si, &pi))
+	{
+		elog(LOG, "CreateProcess call failed: %m (error code %d)",
+			 (int) GetLastError());
+		return -1;
+	}
+
+	if (!save_backend_variables(param, port, pi.hProcess, pi.dwProcessId))
+	{
+		/*
+		 * log made by save_backend_variables, but we have to clean
+		 * up the mess with the half-started process
+		 */
+		if (!TerminateProcess(pi.hProcess, 255))
+			ereport(ERROR,
+					(errmsg_internal("could not terminate unstarted process: error code %d",
+									 (int) GetLastError())));
+		CloseHandle(pi.hProcess);
+		CloseHandle(pi.hThread);
+		return -1;				/* log made by save_backend_variables */
+	}
+
+	/* Drop the shared memory that is now inherited to the backend */
+	if (!UnmapViewOfFile(param))
+		elog(LOG, "could not unmap view of backend parameter file: error code %d",
+			 (int) GetLastError());
+	if (!CloseHandle(paramHandle))
+		elog(LOG, "could not close handle to backend parameter file: error code %d",
+			 (int) GetLastError());
+
+	/*
+	 * Now that the backend variables are written out, we start the
+	 * child thread so it can start initializing while we set up
+	 * the rest of the parent state.
+	 */
+	if (ResumeThread(pi.hThread) == -1)
+	{
+		if (!TerminateProcess(pi.hProcess, 255))
+		{
+			ereport(ERROR,
+					(errmsg_internal("could not terminate unstartable process: error code %d",
+									 (int) GetLastError())));
+			CloseHandle(pi.hProcess);
+			CloseHandle(pi.hThread);
+			return -1;
+		}
+		CloseHandle(pi.hProcess);
+		CloseHandle(pi.hThread);
+		ereport(ERROR,
+				(errmsg_internal("could not resume thread of unstarted process: error code %d",
+								 (int) GetLastError())));
+		return -1;
+	}
+
+	if (!IsUnderPostmaster)
+	{
+		/* We are the Postmaster creating a child... */
+		win32_AddChild(pi.dwProcessId, pi.hProcess);
+	}
+
+	/* Set up the thread to handle the SIGCHLD for this process */
+	if (DuplicateHandle(GetCurrentProcess(),
+						pi.hProcess,
+						GetCurrentProcess(),
+						&childHandleCopy,
+						0,
+						FALSE,
+						DUPLICATE_SAME_ACCESS) == 0)
+		ereport(FATAL,
+				(errmsg_internal("could not duplicate child handle: error code %d",
+								 (int) GetLastError())));
+
+	waiterThread = CreateThread(NULL, 64 * 1024, win32_sigchld_waiter,
+								(LPVOID) childHandleCopy, 0, NULL);
+	if (!waiterThread)
+		ereport(FATAL,
+		   (errmsg_internal("could not create sigchld waiter thread: error code %d",
+							(int) GetLastError())));
+	CloseHandle(waiterThread);
+
+	if (IsUnderPostmaster)
+		CloseHandle(pi.hProcess);
+	CloseHandle(pi.hThread);
+
+	return pi.dwProcessId;
+}
+
+#endif /* WIN32 */
+
+
 /*
  * SubPostmasterMain -- Get the fork/exec'd process into a state equivalent
  *			to what it would be if we'd simply forked on Unix, and then
@@ -2859,6 +3152,19 @@ SubPostmasterMain(int argc, char *argv[])
 
 	MyProcPid = getpid();		/* reset MyProcPid */
 
+	/* Read in file-based context */
+	memset(&port, 0, sizeof(Port));
+	read_backend_variables(argv[2], &port);
+
+	/*
+	 * Start our win32 signal implementation. This has to be done
+	 * after we read the backend variables, because we need to pick
+	 * up the signal pipe from the parent process.
+	 */
+#ifdef WIN32
+	pgwin32_signal_initialize();
+#endif
+
 	/* In EXEC_BACKEND case we will not have inherited these settings */
 	IsPostmasterEnvironment = true;
 	whereToSendOutput = None;
@@ -2873,9 +3179,7 @@ SubPostmasterMain(int argc, char *argv[])
 	if (argc < 3)
 		elog(FATAL, "invalid subpostmaster invocation");
 
-	/* Read in file-based context */
-	memset(&port, 0, sizeof(Port));
-	read_backend_variables(argv[2], &port);
+	/* Read in remaining GUC variables */
 	read_nondefault_variables();
 
 	/* Run backend or appropriate child */
@@ -3297,189 +3601,278 @@ CreateOptsFile(int argc, char *argv[], char *fullprogname)
 #ifdef EXEC_BACKEND
 
 /*
- * The following need to be available to the read/write_backend_variables
+ * The following need to be available to the save/restore_backend_variables
  * functions
  */
-#include "storage/spin.h"
-
 extern slock_t *ShmemLock;
 extern slock_t *ShmemIndexLock;
 extern void *ShmemIndexAlloc;
-typedef struct LWLock LWLock;
 extern LWLock *LWLockArray;
 extern slock_t *ProcStructLock;
 extern int	pgStatSock;
+extern int pgStatPipe[2];
+
+#ifndef WIN32
+#define write_inheritable_socket(dest, src, childpid) (*(dest) = (src))
+#define read_inheritable_socket(dest, src) (*(dest) = *(src))
+#else
+static void write_duplicated_handle(HANDLE *dest, HANDLE src, HANDLE child);
+static void write_inheritable_socket(InheritableSocket *dest, SOCKET src,
+									 pid_t childPid);
+static void read_inheritable_socket(SOCKET *dest, InheritableSocket *src);
+#endif
 
-#define write_var(var,fp) fwrite((void*)&(var),sizeof(var),1,fp)
-#define read_var(var,fp)  fread((void*)&(var),sizeof(var),1,fp)
-#define write_array_var(var,fp) fwrite((void*)(var),sizeof(var),1,fp)
-#define read_array_var(var,fp)	fread((void*)(var),sizeof(var),1,fp)
 
+/* Save critical backend variables into the BackendParameters struct */
+#ifndef WIN32
 static bool
-write_backend_variables(char *filename, Port *port)
+save_backend_variables(BackendParameters *param, Port *port)
+#else
+static bool
+save_backend_variables(BackendParameters *param, Port *port,
+					   HANDLE childProcess, pid_t childPid)
+#endif
 {
-	static unsigned long tmpBackendFileNum = 0;
-	FILE	   *fp;
-	char		str_buf[MAXPGPATH];
+	memcpy(&param->port, port, sizeof(Port));
+	write_inheritable_socket(&param->portsocket, port->sock, childPid);
 
-	/* Calculate name for temp file in caller's buffer */
-	Assert(DataDir);
-	snprintf(filename, MAXPGPATH, "%s/%s/%s.backend_var.%d.%lu",
-			 DataDir, PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX,
-			 MyProcPid, ++tmpBackendFileNum);
+	StrNCpy(param->DataDir, DataDir, MAXPGPATH);
 
-	/* Open file */
-	fp = AllocateFile(filename, PG_BINARY_W);
-	if (!fp)
-	{
-		/* As per OpenTemporaryFile... */
-		char		dirname[MAXPGPATH];
+	memcpy(&param->ListenSocket, &ListenSocket, sizeof(ListenSocket));
 
-		snprintf(dirname, MAXPGPATH, "%s/%s", DataDir, PG_TEMP_FILES_DIR);
-		mkdir(dirname, S_IRWXU);
+	param->MyCancelKey = MyCancelKey;
 
-		fp = AllocateFile(filename, PG_BINARY_W);
-		if (!fp)
-		{
-			ereport(LOG,
-					(errcode_for_file_access(),
-					 errmsg("could not create file \"%s\": %m",
-							filename)));
-			return false;
-		}
-	}
+	param->UsedShmemSegID = UsedShmemSegID;
+	param->UsedShmemSegAddr = UsedShmemSegAddr;
 
-	/* Write vars */
-	write_var(port->sock, fp);
-	write_var(port->proto, fp);
-	write_var(port->laddr, fp);
-	write_var(port->raddr, fp);
-	write_var(port->canAcceptConnections, fp);
-	write_var(port->cryptSalt, fp);
-	write_var(port->md5Salt, fp);
+	param->ShmemLock = ShmemLock;
+	param->ShmemIndexLock = ShmemIndexLock;
+	param->ShmemVariableCache = ShmemVariableCache;
+	param->ShmemIndexAlloc = ShmemIndexAlloc;
+	param->ShmemBackendArray = ShmemBackendArray;
 
-	/*
-	 * XXX FIXME later: writing these strings as MAXPGPATH bytes always is
-	 * probably a waste of resources
-	 */
+	param->LWLockArray = LWLockArray;
+	param->ProcStructLock = ProcStructLock;
+	write_inheritable_socket(&param->pgStatSock, pgStatSock, childPid);
+	write_inheritable_socket(&param->pgStatPipe0, pgStatPipe[0], childPid);
+	write_inheritable_socket(&param->pgStatPipe1, pgStatPipe[1], childPid);
 
-	StrNCpy(str_buf, DataDir, MAXPGPATH);
-	write_array_var(str_buf, fp);
+	param->PostmasterPid = PostmasterPid;
 
-	write_array_var(ListenSocket, fp);
+#ifdef WIN32
+	param->PostmasterHandle = PostmasterHandle;
+	write_duplicated_handle(&param->initial_signal_pipe,
+							pgwin32_create_signal_listener(childPid),
+							childProcess);
+#endif
 
-	write_var(MyCancelKey, fp);
+	memcpy(&param->syslogPipe, &syslogPipe, sizeof(syslogPipe));
 
-	write_var(UsedShmemSegID, fp);
-	write_var(UsedShmemSegAddr, fp);
+	StrNCpy(param->my_exec_path, my_exec_path, MAXPGPATH);
 
-	write_var(ShmemLock, fp);
-	write_var(ShmemIndexLock, fp);
-	write_var(ShmemVariableCache, fp);
-	write_var(ShmemIndexAlloc, fp);
-	write_var(ShmemBackendArray, fp);
+	StrNCpy(param->ExtraOptions, ExtraOptions, MAXPGPATH);
 
-	write_var(LWLockArray, fp);
-	write_var(ProcStructLock, fp);
-	write_var(pgStatSock, fp);
+	StrNCpy(param->lc_collate, setlocale(LC_COLLATE, NULL), MAXPGPATH);
+	StrNCpy(param->lc_ctype, setlocale(LC_CTYPE, NULL), MAXPGPATH);
 
-	write_var(PostmasterPid, fp);
-#ifdef WIN32
-	write_var(PostmasterHandle, fp);
-#endif
+	return true;
+}
 
-	write_var(syslogPipe[0], fp);
-	write_var(syslogPipe[1], fp);
 
-	StrNCpy(str_buf, my_exec_path, MAXPGPATH);
-	write_array_var(str_buf, fp);
+#ifdef WIN32
+/*
+ * Duplicate a handle for usage in a child process, and write the child
+ * process instance of the handle to the parameter file.
+ */
+static void
+write_duplicated_handle(HANDLE *dest, HANDLE src, HANDLE childProcess)
+{
+	HANDLE hChild = INVALID_HANDLE_VALUE;
+
+	if (!DuplicateHandle(GetCurrentProcess(),
+						 src,
+						 childProcess,
+						 &hChild,
+						 0,
+						 TRUE,
+						 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
+		ereport(ERROR,
+				(errmsg_internal("could not duplicate handle to be written to backend parameter file: error code %d",
+								 (int) GetLastError())));
+
+	*dest = hChild;
+}
 
-	write_array_var(ExtraOptions, fp);
+/*
+ * Duplicate a socket for usage in a child process, and write the resulting
+ * structure to the parameter file.
+ * This is required because a number of LSPs (Layered Service Providers) very
+ * common on Windows (antivirus, firewalls, download managers etc) break
+ * straight socket inheritance.
+ */
+static void
+write_inheritable_socket(InheritableSocket *dest, SOCKET src, pid_t childpid)
+{
+	dest->origsocket = src;
+	if (src != 0 && src != -1)
+	{
+		/* Actual socket */
+		if (WSADuplicateSocket(src, childpid, &dest->wsainfo) != 0)
+			ereport(ERROR,
+					(errmsg("could not duplicate socket %d for use in backend: error code %d",
+							src, WSAGetLastError())));
+	}
+}
 
-	StrNCpy(str_buf, setlocale(LC_COLLATE, NULL), MAXPGPATH);
-	write_array_var(str_buf, fp);
-	StrNCpy(str_buf, setlocale(LC_CTYPE, NULL), MAXPGPATH);
-	write_array_var(str_buf, fp);
+/*
+ * Read a duplicate socket structure back, and get the socket descriptor.
+ */
+static void
+read_inheritable_socket(SOCKET *dest, InheritableSocket *src)
+{
+	SOCKET s;
 
-	/* Release file */
-	if (FreeFile(fp))
+	if (src->origsocket == -1  || src->origsocket == 0)
 	{
-		ereport(ERROR,
-				(errcode_for_file_access(),
-				 errmsg("could not write to file \"%s\": %m", filename)));
-		return false;
+		/* Not a real socket! */
+		*dest = src->origsocket;
 	}
+	else
+	{
+		/* Actual socket, so create from structure */
+		s = WSASocket(FROM_PROTOCOL_INFO,
+					  FROM_PROTOCOL_INFO,
+					  FROM_PROTOCOL_INFO,
+					  &src->wsainfo,
+					  0,
+					  0);
+		if (s == INVALID_SOCKET)
+		{
+			write_stderr("could not create inherited socket: error code %d\n",
+						 WSAGetLastError());
+			exit(1);
+		}
+		*dest = s;
 
-	return true;
+		/*
+		 * To make sure we don't get two references to the same socket,
+		 * close the original one. (This would happen when inheritance
+		 * actually works..
+		 */
+		closesocket(src->origsocket);
+	}
 }
+#endif
 
 static void
-read_backend_variables(char *filename, Port *port)
+read_backend_variables(char *id, Port *port)
 {
-	FILE	   *fp;
-	char		str_buf[MAXPGPATH];
+#ifndef WIN32
+	/* Non-win32 implementation reads from file */
+	FILE *fp;
+	BackendParameters param;
 
 	/* Open file */
-	fp = AllocateFile(filename, PG_BINARY_R);
+	fp = AllocateFile(id, PG_BINARY_R);
 	if (!fp)
-		ereport(FATAL,
-				(errcode_for_file_access(),
-		  errmsg("could not read from backend variables file \"%s\": %m",
-				 filename)));
+	{
+		write_stderr("could not read from backend variables file \"%s\": %s\n",
+					 id, strerror(errno));
+		exit(1);
+	}
+
+	if (fread(&param, sizeof(param), 1, fp) != 1)
+	{
+		write_stderr("could not read from backend variables file \"%s\": %s\n",
+					 id, strerror(errno));
+		exit(1);
+	}
+
+	/* Release file */
+	FreeFile(fp);
+	if (unlink(id) != 0)
+	{
+		write_stderr("could not remove file \"%s\": %s\n",
+					 id, strerror(errno));
+		exit(1);
+	}
 
-	/* Read vars */
-	read_var(port->sock, fp);
-	read_var(port->proto, fp);
-	read_var(port->laddr, fp);
-	read_var(port->raddr, fp);
-	read_var(port->canAcceptConnections, fp);
-	read_var(port->cryptSalt, fp);
-	read_var(port->md5Salt, fp);
+	restore_backend_variables(&param, port);
+#else
+	/* Win32 version uses mapped file */
+	HANDLE paramHandle;
+	BackendParameters *param;
 
-	read_array_var(str_buf, fp);
-	SetDataDir(str_buf);
+	paramHandle = (HANDLE)atol(id);
+	param = MapViewOfFile(paramHandle, FILE_MAP_READ, 0, 0, 0);
+	if (!param)
+	{
+		write_stderr("could not map view of backend variables: error code %d\n",
+					 (int) GetLastError());
+		exit(1);
+	}
 
-	read_array_var(ListenSocket, fp);
+	restore_backend_variables(param, port);
 
-	read_var(MyCancelKey, fp);
+	if (!UnmapViewOfFile(param))
+	{
+		write_stderr("could not unmap view of backend variables: error code %d\n",
+					 (int) GetLastError());
+		exit(1);
+	}
 
-	read_var(UsedShmemSegID, fp);
-	read_var(UsedShmemSegAddr, fp);
+	if (!CloseHandle(paramHandle))
+	{
+		write_stderr("could not close handle to backend parameter variables: error code %d\n",
+					 (int) GetLastError());
+		exit(1);
+	}
+#endif
+}
+
+/* Restore critical backend variables from the BackendParameters struct */
+static void
+restore_backend_variables(BackendParameters *param, Port *port)
+{
+	memcpy(port, &param->port, sizeof(Port));
+	read_inheritable_socket(&port->sock, &param->portsocket);
+
+	SetDataDir(param->DataDir);
 
-	read_var(ShmemLock, fp);
-	read_var(ShmemIndexLock, fp);
-	read_var(ShmemVariableCache, fp);
-	read_var(ShmemIndexAlloc, fp);
-	read_var(ShmemBackendArray, fp);
+	memcpy(&ListenSocket, &param->ListenSocket, sizeof(ListenSocket));
 
-	read_var(LWLockArray, fp);
-	read_var(ProcStructLock, fp);
-	read_var(pgStatSock, fp);
+	MyCancelKey = param->MyCancelKey;
+
+	UsedShmemSegID = param->UsedShmemSegID;
+	UsedShmemSegAddr = param->UsedShmemSegAddr;
+
+	ShmemLock = param->ShmemLock;
+	ShmemIndexLock = param->ShmemIndexLock;
+	ShmemVariableCache = param->ShmemVariableCache;
+	ShmemIndexAlloc = param->ShmemIndexAlloc;
+	ShmemBackendArray = param->ShmemBackendArray;
+
+	LWLockArray = param->LWLockArray;
+	ProcStructLock = param->ProcStructLock;
+	read_inheritable_socket(&pgStatSock, &param->pgStatSock);
+	read_inheritable_socket(&pgStatPipe[0], &param->pgStatPipe0);
+	read_inheritable_socket(&pgStatPipe[1], &param->pgStatPipe1);
+
+	PostmasterPid = param->PostmasterPid;
 
-	read_var(PostmasterPid, fp);
 #ifdef WIN32
-	read_var(PostmasterHandle, fp);
+	PostmasterHandle = param->PostmasterHandle;
+	pgwin32_initial_signal_pipe = param->initial_signal_pipe;
 #endif
 
-	read_var(syslogPipe[0], fp);
-	read_var(syslogPipe[1], fp);
-
-	read_array_var(str_buf, fp);
-	StrNCpy(my_exec_path, str_buf, MAXPGPATH);
+	memcpy(&syslogPipe, &param->syslogPipe, sizeof(syslogPipe));
 
-	read_array_var(ExtraOptions, fp);
+	StrNCpy(my_exec_path, param->my_exec_path, MAXPGPATH);
 
-	read_array_var(str_buf, fp);
-	setlocale(LC_COLLATE, str_buf);
-	read_array_var(str_buf, fp);
-	setlocale(LC_CTYPE, str_buf);
+	StrNCpy(ExtraOptions, param->ExtraOptions, MAXPGPATH);
 
-	/* Release file */
-	FreeFile(fp);
-	if (unlink(filename) != 0)
-		ereport(WARNING,
-				(errcode_for_file_access(),
-				 errmsg("could not remove file \"%s\": %m", filename)));
+	setlocale(LC_COLLATE, param->lc_collate);
+	setlocale(LC_CTYPE, param->lc_ctype);
 }
 
 
@@ -3542,74 +3935,6 @@ ShmemBackendArrayRemove(pid_t pid)
 
 #ifdef WIN32
 
-static pid_t
-win32_forkexec(const char *path, char *argv[])
-{
-	STARTUPINFO si;
-	PROCESS_INFORMATION pi;
-	int			i;
-	int			j;
-	char		cmdLine[MAXPGPATH * 2];
-	HANDLE		childHandleCopy;
-	HANDLE		waiterThread;
-
-	/* Format the cmd line */
-	cmdLine[sizeof(cmdLine) - 1] = '\0';
-	cmdLine[sizeof(cmdLine) - 2] = '\0';
-	snprintf(cmdLine, sizeof(cmdLine) - 1, "\"%s\"", path);
-	i = 0;
-	while (argv[++i] != NULL)
-	{
-		j = strlen(cmdLine);
-		snprintf(cmdLine + j, sizeof(cmdLine) - 1 - j, " \"%s\"", argv[i]);
-	}
-	if (cmdLine[sizeof(cmdLine) - 2] != '\0')
-	{
-		elog(LOG, "subprocess command line too long");
-		return -1;
-	}
-
-	memset(&pi, 0, sizeof(pi));
-	memset(&si, 0, sizeof(si));
-	si.cb = sizeof(si);
-	if (!CreateProcess(NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
-	{
-		elog(LOG, "CreateProcess call failed (%d): %m", (int) GetLastError());
-		return -1;
-	}
-
-	if (!IsUnderPostmaster)
-	{
-		/* We are the Postmaster creating a child... */
-		win32_AddChild(pi.dwProcessId, pi.hProcess);
-	}
-
-	if (DuplicateHandle(GetCurrentProcess(),
-						pi.hProcess,
-						GetCurrentProcess(),
-						&childHandleCopy,
-						0,
-						FALSE,
-						DUPLICATE_SAME_ACCESS) == 0)
-		ereport(FATAL,
-				(errmsg_internal("could not duplicate child handle: %d",
-								 (int) GetLastError())));
-
-	waiterThread = CreateThread(NULL, 64 * 1024, win32_sigchld_waiter,
-								(LPVOID) childHandleCopy, 0, NULL);
-	if (!waiterThread)
-		ereport(FATAL,
-		   (errmsg_internal("could not create sigchld waiter thread: %d",
-							(int) GetLastError())));
-	CloseHandle(waiterThread);
-
-	if (IsUnderPostmaster)
-		CloseHandle(pi.hProcess);
-	CloseHandle(pi.hThread);
-
-	return pi.dwProcessId;
-}
-
 /*
  * Note: The following three functions must not be interrupted (eg. by
  * signals).  As the Postgres Win32 signalling architecture (currently)
@@ -3687,7 +4012,7 @@ win32_waitpid(int *exitstatus)
 		{
 			case WAIT_FAILED:
 				ereport(LOG,
-						(errmsg_internal("failed to wait on %lu of %lu children: %d",
+						(errmsg_internal("failed to wait on %lu of %lu children: error code %d",
 						 num, win32_numChildren, (int) GetLastError())));
 				return -1;
 
@@ -3712,7 +4037,7 @@ win32_waitpid(int *exitstatus)
 					 */
 					ereport(FATAL,
 							(errmsg_internal("failed to get exit code for child %lu",
-										   win32_childPIDArray[index])));
+											 (DWORD)win32_childPIDArray[index])));
 				}
 				*exitstatus = (int) exitCode;
 				return win32_childPIDArray[index];
diff --git a/src/include/port/win32.h b/src/include/port/win32.h
index 7960879dafa..61e7c3f74f0 100644
--- a/src/include/port/win32.h
+++ b/src/include/port/win32.h
@@ -1,4 +1,4 @@
-/* $PostgreSQL: pgsql/src/include/port/win32.h,v 1.39 2004/10/06 17:47:53 momjian Exp $ */
+/* $PostgreSQL: pgsql/src/include/port/win32.h,v 1.40 2004/11/17 00:14:14 tgl Exp $ */
 
 /* undefine and redefine after #include */
 #undef mkdir
@@ -104,8 +104,11 @@ int			semop(int semId, struct sembuf * sops, int flag);
 
 
 /* In backend/port/win32/signal.c */
-void		pgwin32_signal_initialize(void);
 extern DLLIMPORT HANDLE pgwin32_signal_event;
+extern HANDLE pgwin32_initial_signal_pipe;
+
+void		pgwin32_signal_initialize(void);
+HANDLE		pgwin32_create_signal_listener(pid_t pid);
 void		pgwin32_dispatch_queued_signals(void);
 void		pg_queue_signal(int signum);
 
-- 
GitLab