From 2a3a35f2e80cb2badcb0efbce1bad2484e364b7b Mon Sep 17 00:00:00 2001
From: Mike Palmiotto <mike.palmiotto@crunchydata.com>
Date: Fri, 27 Sep 2019 12:28:19 -0400
Subject: [PATCH 1/8] Add ForkProcType infrastructure

---
 src/backend/bootstrap/bootstrap.c     |   1 +
 src/backend/main/main.c               |   7 +
 src/backend/postmaster/postmaster.c   | 197 ++++++++++++++++----------
 src/backend/utils/init/globals.c      |   3 +-
 src/backend/utils/init/postinit.c     |   2 +-
 src/include/bootstrap/bootstrap.h     |   2 +-
 src/include/postmaster/fork_process.h |  33 +++++
 7 files changed, 167 insertions(+), 78 deletions(-)

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 9238fbe98d..9f3dad1c6d 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -70,6 +70,7 @@ static void cleanup(void);
  */
 
 AuxProcType MyAuxProcType = NotAnAuxProcess;	/* declared in miscadmin.h */
+ForkProcType MyForkProcType = NoForkProcess;	/* declared in fork_process.h */
 
 Relation	boot_reldesc;		/* current relation descriptor */
 
diff --git a/src/backend/main/main.c b/src/backend/main/main.c
index a9edbfd4a4..79942deb7d 100644
--- a/src/backend/main/main.c
+++ b/src/backend/main/main.c
@@ -199,7 +199,14 @@ main(int argc, char *argv[])
 #endif
 
 	if (argc > 1 && strcmp(argv[1], "--boot") == 0)
+	{
+		MemoryContext		old_ctx;
+
+		old_ctx = MemoryContextSwitchTo(TopMemoryContext);
+
 		AuxiliaryProcessMain(argc, argv);	/* does not return */
+		MemoryContextSwitchTo(old_ctx);
+	}
 	else if (argc > 1 && strcmp(argv[1], "--describe-config") == 0)
 		GucInfoMain();			/* does not return */
 	else if (argc > 1 && strcmp(argv[1], "--single") == 0)
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index eb9e0221f8..e0d11cc6ab 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -421,7 +421,7 @@ static int	CountChildren(int target);
 static bool assign_backendlist_entry(RegisteredBgWorker *rw);
 static void maybe_start_bgworkers(void);
 static bool CreateOptsFile(int argc, char *argv[], char *fullprogname);
-static pid_t StartChildProcess(AuxProcType type);
+static pid_t StartChildProcess(ForkProcType type);
 static void StartAutovacuumWorker(void);
 static void MaybeStartWalReceiver(void);
 static void InitPostmasterDeathWatchHandle(void);
@@ -456,7 +456,7 @@ typedef struct
 #endif							/* WIN32 */
 
 static pid_t backend_forkexec(Port *port);
-static pid_t internal_forkexec(int argc, char *argv[], Port *port);
+static pid_t internal_forkexec(int argc, char *argv[]);
 
 /* Type for a socket that can be inherited to a client process */
 #ifdef WIN32
@@ -522,6 +522,7 @@ typedef struct
 	char		my_exec_path[MAXPGPATH];
 	char		pkglib_path[MAXPGPATH];
 	char		ExtraOptions[MAXPGPATH];
+	ForkProcType	proc_type;
 } BackendParameters;
 
 static void read_backend_variables(char *id, Port *port);
@@ -538,11 +539,11 @@ static void ShmemBackendArrayAdd(Backend *bn);
 static void ShmemBackendArrayRemove(Backend *bn);
 #endif							/* EXEC_BACKEND */
 
-#define StartupDataBase()		StartChildProcess(StartupProcess)
-#define StartBackgroundWriter() StartChildProcess(BgWriterProcess)
-#define StartCheckpointer()		StartChildProcess(CheckpointerProcess)
-#define StartWalWriter()		StartChildProcess(WalWriterProcess)
-#define StartWalReceiver()		StartChildProcess(WalReceiverProcess)
+#define StartupDataBase()		StartChildProcess(StartupFork)
+#define StartBackgroundWriter() StartChildProcess(BgWriterFork)
+#define StartCheckpointer()		StartChildProcess(CheckpointerFork)
+#define StartWalWriter()		StartChildProcess(WalWriterFork)
+#define StartWalReceiver()		StartChildProcess(WalReceiverFork)
 
 /* Macros to check exit status of a child process */
 #define EXIT_STATUS_0(st)  ((st) == 0)
@@ -560,6 +561,60 @@ int			postmaster_alive_fds[2] = {-1, -1};
 HANDLE		PostmasterHandle;
 #endif
 
+static Port *ConnProcPort = NULL;
+
+/*
+ * PrepAuxProcessFork
+ *
+ * Prepare a ForkProcType struct for the auxiliary process specified by
+ * AuxProcType. This does all prep related to av parameters and error messages.
+ */
+static void
+PrepAuxProcessFork(ForkProcData *aux_fork)
+{
+	int			ac = 0;
+
+	/*
+	 * Set up command-line arguments for subprocess
+	 */
+	aux_fork->av[ac++] = pstrdup("postgres");
+
+#ifdef EXEC_BACKEND
+	aux_fork->av[ac++] = pstrdup("--forkboot");
+	aux_fork->av[ac++] = NULL;			/* filled in by postmaster_forkexec */
+#endif
+
+	aux_fork->av[ac++] = psprintf("-x%d", MyForkProcType);
+
+	aux_fork->av[ac] = NULL;
+	Assert(ac < lengthof(*aux_fork->av));
+
+	aux_fork->ac = ac;
+	switch (MyForkProcType)
+	{
+		case StartupProcess:
+			aux_fork->type_desc = pstrdup("startup");
+			break;
+		case BgWriterProcess:
+			aux_fork->type_desc = pstrdup("background writer");
+			break;
+		case CheckpointerProcess:
+			aux_fork->type_desc = pstrdup("checkpointer");
+			break;
+		case WalWriterProcess:
+			aux_fork->type_desc = pstrdup("WAL writer");
+			break;
+		case WalReceiverProcess:
+			aux_fork->type_desc = pstrdup("WAL receiver");
+			break;
+		default:
+			aux_fork->type_desc = pstrdup("child");
+			break;
+	}
+
+	aux_fork->child_main = AuxiliaryProcessMain;
+}
+
 /*
  * Postmaster main entry point
  */
@@ -1710,19 +1765,17 @@ ServerLoop(void)
 					break;
 				if (FD_ISSET(ListenSocket[i], &rmask))
 				{
-					Port	   *port;
-
-					port = ConnCreate(ListenSocket[i]);
-					if (port)
+					ConnProcPort = ConnCreate(ListenSocket[i]);
+					if (ConnProcPort)
 					{
-						BackendStartup(port);
+						BackendStartup(ConnProcPort);
 
 						/*
 						 * We no longer need the open socket or port structure
 						 * in this process
 						 */
-						StreamClose(port->sock);
-						ConnFree(port);
+						StreamClose(ConnProcPort->sock);
+						ConnFree(ConnProcPort);
 					}
 				}
 			}
@@ -4477,11 +4530,11 @@ BackendRun(Port *port)
 pid_t
 postmaster_forkexec(int argc, char *argv[])
 {
-	Port		port;
-
 	/* This entry point passes dummy values for the Port variables */
-	memset(&port, 0, sizeof(port));
-	return internal_forkexec(argc, argv, &port);
+	if (!ConnProcPort)
+		ConnProcPort = palloc0(sizeof(*ConnProcPort));
+
+	return internal_forkexec(argc, argv);
 }
 
 /*
@@ -4506,7 +4559,7 @@ backend_forkexec(Port *port)
 	av[ac] = NULL;
 	Assert(ac < lengthof(av));
 
-	return internal_forkexec(ac, av, port);
+	return internal_forkexec(ac, av);
 }
 
 #ifndef WIN32
@@ -4518,7 +4571,7 @@ backend_forkexec(Port *port)
  * - fork():s, and then exec():s the child process
  */
 static pid_t
-internal_forkexec(int argc, char *argv[], Port *port)
+internal_forkexec(int argc, char *argv[])
 {
 	static unsigned long tmpBackendFileNum = 0;
 	pid_t		pid;
@@ -4526,7 +4579,7 @@ internal_forkexec(int argc, char *argv[], Port *port)
 	BackendParameters param;
 	FILE	   *fp;
 
-	if (!save_backend_variables(&param, port))
+	if (!save_backend_variables(&param, ConnProcPort))
 		return -1;				/* log made by save_backend_variables */
 
 	/* Calculate name for temp file */
@@ -4836,9 +4889,12 @@ SubPostmasterMain(int argc, char *argv[])
 	if (argc < 3)
 		elog(FATAL, "invalid subpostmaster invocation");
 
+	Assert(!ConnProcPort);
+
 	/* Read in the variables file */
 	memset(&port, 0, sizeof(Port));
 	read_backend_variables(argv[2], &port);
+	ConnProcPort = &port;
 
 	/* Close the postmaster's sockets (as soon as we know them) */
 	ClosePostmasterPorts(strcmp(argv[1], "--forklog") == 0);
@@ -5360,7 +5416,6 @@ CountChildren(int target)
 	return cnt;
 }
 
-
 /*
  * StartChildProcess -- start an auxiliary process for the postmaster
  *
@@ -5371,36 +5426,50 @@ CountChildren(int target)
  * to start subprocess.
  */
 static pid_t
-StartChildProcess(AuxProcType type)
+StartChildProcess(ForkProcType type)
 {
 	pid_t		pid;
-	char	   *av[10];
-	int			ac = 0;
-	char		typebuf[32];
-
-	/*
-	 * Set up command-line arguments for subprocess
-	 */
-	av[ac++] = "postgres";
+	ForkProcData	   *fork_data;
+	MemoryContext		old_ctx;
+
+	/* Keep fork data after splitting from postmaster */
+	old_ctx = MemoryContextSwitchTo(TopMemoryContext);
+	fork_data = (ForkProcData *) palloc0(sizeof(ForkProcData));
+	fork_data->av = palloc0(sizeof(char *) * 10);
+	MyForkProcType = type;
+
+	switch(type)
+	{
+		/* Auxiliary Processes */
+		case CheckerFork:
+		case BootstrapFork:
+		case StartupFork:
+		case BgWriterFork:
+		case CheckpointerFork:
+		case WalWriterFork:
+		case WalReceiverFork:
+			PrepAuxProcessFork(fork_data);
+			break;
 
-#ifdef EXEC_BACKEND
-	av[ac++] = "--forkboot";
-	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
-#endif
+		default:
+			break;
+	}
 
-	snprintf(typebuf, sizeof(typebuf), "-x%d", type);
-	av[ac++] = typebuf;
+	MemoryContextSwitchTo(old_ctx);
 
-	av[ac] = NULL;
-	Assert(ac < lengthof(av));
+	/* Bail out if pre-conditions are not met */
+	if (fork_data == NULL)
+		return 0;
 
 #ifdef EXEC_BACKEND
-	pid = postmaster_forkexec(ac, av);
-#else							/* !EXEC_BACKEND */
+	pid = postmaster_forkexec(fork_data->ac, fork_data->av);
+#else
 	pid = fork_process();
+#endif
 
 	if (pid == 0)				/* child */
 	{
+#ifndef EXEC_BACKEND
 		InitPostmasterChild();
 
 		/* Close the postmaster's sockets */
@@ -5410,52 +5479,30 @@ StartChildProcess(AuxProcType type)
 		MemoryContextSwitchTo(TopMemoryContext);
 		MemoryContextDelete(PostmasterContext);
 		PostmasterContext = NULL;
+#endif
+		/* Call the process's main subroutine */
+		fork_data->child_main (fork_data->ac, fork_data->av);
 
-		AuxiliaryProcessMain(ac, av);
+#ifndef EXEC_BACKEND
 		ExitPostmaster(0);
+#endif
 	}
-#endif							/* EXEC_BACKEND */
-
-	if (pid < 0)
+	else if (pid < 0)
 	{
 		/* in parent, fork failed */
-		int			save_errno = errno;
+		int save_errno = errno;
 
-		errno = save_errno;
-		switch (type)
-		{
-			case StartupProcess:
-				ereport(LOG,
-						(errmsg("could not fork startup process: %m")));
-				break;
-			case BgWriterProcess:
-				ereport(LOG,
-						(errmsg("could not fork background writer process: %m")));
-				break;
-			case CheckpointerProcess:
-				ereport(LOG,
-						(errmsg("could not fork checkpointer process: %m")));
-				break;
-			case WalWriterProcess:
-				ereport(LOG,
-						(errmsg("could not fork WAL writer process: %m")));
-				break;
-			case WalReceiverProcess:
-				ereport(LOG,
-						(errmsg("could not fork WAL receiver process: %m")));
-				break;
-			default:
-				ereport(LOG,
-						(errmsg("could not fork process: %m")));
-				break;
-		}
+		errno =  save_errno;
+		ereport(LOG,
+			(errmsg("could not fork %s process: %m", fork_data->type_desc)));
 
 		/*
 		 * fork failure is fatal during startup, but there's no need to choke
 		 * immediately if starting other child types fails.
 		 */
-		if (type == StartupProcess)
+		if (MyForkProcType == StartupFork)
 			ExitPostmaster(1);
+
 		return 0;
 	}
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 3bf96de256..51657924f1 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -38,9 +38,10 @@ volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
 
 int			MyProcPid;
+int			MyChildProcPid;
 pg_time_t	MyStartTime;
 TimestampTz MyStartTimestamp;
-struct Port *MyProcPort;
+struct Port *MyProcPort = NULL;
 int32		MyCancelKey;
 int			MyPMChildSlot;
 
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 29c5ec7b58..c0840c33a7 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1113,7 +1113,7 @@ process_startup_options(Port *port, bool am_superuser)
 		av = (char **) palloc(maxac * sizeof(char *));
 		ac = 0;
 
-		av[ac++] = "postgres";
+		av[ac++] = pstrdup("postgres");
 
 		pg_split_opts(av, &ac, port->cmdline_options);
 
diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h
index 85705602cb..3c53454069 100644
--- a/src/include/bootstrap/bootstrap.h
+++ b/src/include/bootstrap/bootstrap.h
@@ -15,7 +15,7 @@
 #define BOOTSTRAP_H
 
 #include "nodes/execnodes.h"
-
+#include "postmaster/fork_process.h"
 
 /*
  * MAXATTR is the maximum number of attributes in a relation supported
diff --git a/src/include/postmaster/fork_process.h b/src/include/postmaster/fork_process.h
index eb3c8ec974..d912229055 100644
--- a/src/include/postmaster/fork_process.h
+++ b/src/include/postmaster/fork_process.h
@@ -12,6 +12,39 @@
 #ifndef FORK_PROCESS_H
 #define FORK_PROCESS_H
 
+#include "postmaster.h"
+
+typedef enum
+{
+   NoForkProcess = -1,
+   CheckerFork = 0,
+   BootstrapFork,
+   StartupFork,
+   BgWriterFork,
+   CheckpointerFork,
+   WalWriterFork,
+   WalReceiverFork,	/* end of Auxiliary Process Forks */
+
+   NUMFORKPROCTYPES			/* Must be last! */
+} ForkProcType;
+
+extern ForkProcType MyForkProcType;
+
+struct ForkProcData;
+
+typedef void (*ChildProcessMain) (int argc, char *argv[]);
+
+typedef struct ForkProcData
+{
+   char                   **av;
+   int                     ac;
+   int                     save_errno; /* for failure */
+   char                   *type_desc;
+   ChildProcessMain        postmaster_main;
+   ChildProcessMain        child_main;
+   ChildProcessMain        fail_main;
+} ForkProcData;
+
 extern pid_t fork_process(void);
 
 #endif							/* FORK_PROCESS_H */
-- 
2.23.0

