From 205ea86dde898f7edac327d46b2b43b04721aadc Mon Sep 17 00:00:00 2001
From: Mike Palmiotto <mike.palmiotto@crunchydata.com>
Date: Mon, 30 Sep 2019 14:42:53 -0400
Subject: [PATCH 7/8] Add backends to process centralization

---
 src/backend/postmaster/postmaster.c   | 309 +++++++++++++-------------
 src/include/postmaster/fork_process.h |   5 +
 src/include/postmaster/postmaster.h   |   1 -
 3 files changed, 160 insertions(+), 155 deletions(-)

diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 8f862fcd64..b55cc4556d 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -144,6 +144,7 @@ static Backend *ShmemBackendArray;
 
 BackgroundWorker *MyBgworkerEntry = NULL;
 RegisteredBgWorker *CurrentBgWorker = NULL;
+static Backend	   *MyBackend;
 static int			child_errno;
 
 /* The socket number we are listening for connections on */
@@ -356,7 +357,6 @@ static void BackendInitialize(Port *port);
 static void BackendRun(Port *port) pg_attribute_noreturn();
 static void ExitPostmaster(int status) pg_attribute_noreturn();
 static int	ServerLoop(void);
-static int	BackendStartup(Port *port);
 static int	ProcessStartupPacket(Port *port, bool secure_done);
 static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options);
 static void processCancelRequest(Port *port, void *pkt);
@@ -409,7 +409,6 @@ typedef struct
 } win32_deadchild_waitinfo;
 #endif							/* WIN32 */
 
-static pid_t backend_forkexec(Port *port);
 static pid_t internal_forkexec(int argc, char *argv[]);
 
 /* Type for a socket that can be inherited to a client process */
@@ -505,6 +504,7 @@ static void ShmemBackendArrayRemove(Backend *bn);
 #define pgarch_start()			StartChildProcess(PgArchiverFork)
 #define SysLogger_Start()		StartChildProcess(SysLoggerFork)
 #define do_start_bgworker()		StartChildProcess(BgWorkerFork)
+#define BackendStartup()		StartChildProcess(BackendFork)
 
 /* Macros to check exit status of a child process */
 #define EXIT_STATUS_0(st)  ((st) == 0)
@@ -524,6 +524,141 @@ HANDLE		PostmasterHandle;
 
 static Port *ConnProcPort = NULL;
 
+/*
+ *	 BackendMain
+ *
+ *	 Child code when forking a Backend.
+ */
+static void BackendMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
+{
+	/*
+	 * Perform additional initialization and collect startup packet.
+	 *
+	 * We want to do this before InitProcess() for a couple of reasons: 1.
+	 * so that we aren't eating up a PGPROC slot while waiting on the
+	 * client. 2. so that if InitProcess() fails due to being out of
+	 * PGPROC slots, we have already initialized libpq and are able to
+	 * report the error to the client.
+	 */
+	BackendInitialize(ConnProcPort);
+
+#ifdef EXEC_BACKEND
+	shmemSetup(false);
+#endif
+
+	/* And run the backend */
+	BackendRun(ConnProcPort);		/* does not return */
+}
+
+/*
+ *	 BackendPostmasterMain
+ *
+ *	 Parent code when forking a Backend.
+ */
+static void BackendPostmasterMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
+{
+	/* in parent, successful fork */
+	ereport(DEBUG2,
+			(errmsg_internal("forked new backend, pid=%d socket=%d",
+							 (int) MyChildProcPid, (int) MyProcPort->sock)));
+
+	/*
+	 * Everything's been successful, it's safe to add this backend to our list
+	 * of backends.
+	 */
+	MyBackend->pid = MyChildProcPid;
+	MyBackend->bkend_type = BACKEND_TYPE_NORMAL;	/* Can change later to WALSND */
+	dlist_push_head(&BackendList, &MyBackend->elem);
+
+#ifdef EXEC_BACKEND
+	if (!MyBackend->dead_end)
+		ShmemBackendArrayAdd(MyBackend);
+#endif
+}
+
+/*
+ *	 BackendFailFork
+ *
+ *	 Backend cleanup in case a failure occurs forking a new Backend.
+ */
+static void BackendFailFork(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
+{
+	if (!MyBackend->dead_end)
+		(void) ReleasePostmasterChildSlot(MyBackend->child_slot);
+	free(MyBackend);
+
+	report_fork_failure_to_client(MyProcPort, child_errno);
+}
+
+/*
+ * PrepBackendFork
+ *
+ * Prepare a ForkProcType struct for starting a Backend.
+ * This does all prep related to av parameters and error messages.
+*/
+static void
+PrepBackendFork(ForkProcData *backend_fork)
+{
+	int			ac = 0;
+
+	/*
+	 * Create backend data structure.  Better before the fork() so we can
+	 * handle failure cleanly.
+	 */
+	MyBackend = (Backend *) malloc(sizeof(Backend));
+	if (!MyBackend)
+	{
+		ereport(LOG,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory")));
+	}
+
+#ifdef EXEC_BACKEND
+	backend_fork->av[ac++] = pstrdup("postgres");
+	backend_fork->av[ac++] = pstrdup("--forkbackend");
+	backend_fork->av[ac++] = NULL;
+#endif
+	backend_fork->av[ac] = NULL;
+	backend_fork->ac = ac;
+
+	Assert(ac < lengthof(*backend_fork->av));
+
+	/*
+	 * Compute the cancel key that will be assigned to this backend. The
+	 * backend will have its own copy in the forked-off process' value of
+	 * MyCancelKey, so that it can transmit the key to the frontend.
+	 */
+	if (!RandomCancelKey(&MyCancelKey))
+	{
+		free(MyBackend);
+		ereport(LOG,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("could not generate random cancel key")));
+	}
+
+	MyBackend->cancel_key = MyCancelKey;
+
+	/* Pass down canAcceptConnections state */
+	ConnProcPort->canAcceptConnections = canAcceptConnections();
+	MyBackend->dead_end = (ConnProcPort->canAcceptConnections != CAC_OK &&
+						   ConnProcPort->canAcceptConnections != CAC_WAITBACKUP);
+
+	/*
+	 * Unless it's a dead_end child, assign it a child slot number
+	 */
+	if (!MyBackend->dead_end)
+		MyBackend->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
+	else
+		MyBackend->child_slot = 0;
+
+	/* Hasn't asked to be notified about any bgworkers yet */
+	MyBackend->bgworker_notify = false;
+
+	backend_fork->postmaster_main = BackendPostmasterMain;
+	backend_fork->child_main = BackendMain;
+	backend_fork->fail_main = BackendFailFork;
+}
+
 /*
  * PrepAuxProcessFork
  *
@@ -1729,7 +1864,7 @@ ServerLoop(void)
 					ConnProcPort = ConnCreate(ListenSocket[i]);
 					if (ConnProcPort)
 					{
-						BackendStartup(ConnProcPort);
+						BackendStartup();
 
 						/*
 						 * We no longer need the open socket or port structure
@@ -4085,122 +4220,6 @@ TerminateChildren(int signal)
 		signal_child(PgStatPID, signal);
 }
 
-/*
- * BackendStartup -- start backend process
- *
- * returns: STATUS_ERROR if the fork failed, STATUS_OK otherwise.
- *
- * Note: if you change this code, also consider StartAutovacuumWorker.
- */
-static int
-BackendStartup(Port *port)
-{
-	Backend    *bn;				/* for backend cleanup */
-	pid_t		pid;
-
-	/*
-	 * Create backend data structure.  Better before the fork() so we can
-	 * handle failure cleanly.
-	 */
-	bn = (Backend *) malloc(sizeof(Backend));
-	if (!bn)
-	{
-		ereport(LOG,
-				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("out of memory")));
-		return STATUS_ERROR;
-	}
-
-	/*
-	 * Compute the cancel key that will be assigned to this backend. The
-	 * backend will have its own copy in the forked-off process' value of
-	 * MyCancelKey, so that it can transmit the key to the frontend.
-	 */
-	if (!RandomCancelKey(&MyCancelKey))
-	{
-		free(bn);
-		ereport(LOG,
-				(errcode(ERRCODE_INTERNAL_ERROR),
-				 errmsg("could not generate random cancel key")));
-		return STATUS_ERROR;
-	}
-
-	bn->cancel_key = MyCancelKey;
-
-	/* Pass down canAcceptConnections state */
-	port->canAcceptConnections = canAcceptConnections();
-	bn->dead_end = (port->canAcceptConnections != CAC_OK &&
-					port->canAcceptConnections != CAC_WAITBACKUP);
-
-	/*
-	 * Unless it's a dead_end child, assign it a child slot number
-	 */
-	if (!bn->dead_end)
-		bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
-	else
-		bn->child_slot = 0;
-
-	/* Hasn't asked to be notified about any bgworkers yet */
-	bn->bgworker_notify = false;
-
-#ifdef EXEC_BACKEND
-	pid = backend_forkexec(port);
-#else							/* !EXEC_BACKEND */
-	pid = fork_process();
-	if (pid == 0)				/* child */
-	{
-		free(bn);
-
-		/* Detangle from postmaster */
-		InitPostmasterChild();
-
-		/* Close the postmaster's sockets */
-		ClosePostmasterPorts(false);
-
-		/* Perform additional initialization and collect startup packet */
-		BackendInitialize(port);
-
-		/* And run the backend */
-		BackendRun(port);
-	}
-#endif							/* EXEC_BACKEND */
-
-	if (pid < 0)
-	{
-		/* in parent, fork failed */
-		int			save_errno = errno;
-
-		if (!bn->dead_end)
-			(void) ReleasePostmasterChildSlot(bn->child_slot);
-		free(bn);
-		errno = save_errno;
-		ereport(LOG,
-				(errmsg("could not fork new process for connection: %m")));
-		report_fork_failure_to_client(port, save_errno);
-		return STATUS_ERROR;
-	}
-
-	/* in parent, successful fork */
-	ereport(DEBUG2,
-			(errmsg_internal("forked new backend, pid=%d socket=%d",
-							 (int) pid, (int) port->sock)));
-
-	/*
-	 * Everything's been successful, it's safe to add this backend to our list
-	 * of backends.
-	 */
-	bn->pid = pid;
-	bn->bkend_type = BACKEND_TYPE_NORMAL;	/* Can change later to WALSND */
-	dlist_push_head(&BackendList, &bn->elem);
-
-#ifdef EXEC_BACKEND
-	if (!bn->dead_end)
-		ShmemBackendArrayAdd(bn);
-#endif
-
-	return STATUS_OK;
-}
-
 /*
  * Try to report backend fork() failure to client before we close the
  * connection.  Since we do not care to risk blocking the postmaster on
@@ -4440,7 +4459,7 @@ BackendRun(Port *port)
 									  maxac * sizeof(char *));
 	ac = 0;
 
-	av[ac++] = "postgres";
+	av[ac++] = pstrdup("postgres");
 
 	/*
 	 * Pass any backend switches specified with -o on the postmaster's own
@@ -4489,38 +4508,13 @@ BackendRun(Port *port)
  * child process.
  */
 pid_t
-postmaster_forkexec(int argc, char *argv[])
+postmaster_forkexec(ForkProcData *fork_data)
 {
 	/* This entry point passes dummy values for the Port variables */
 	if (!ConnProcPort)
 		ConnProcPort = palloc0(sizeof(*ConnProcPort));
 
-	return internal_forkexec(argc, argv);
-}
-
-/*
- * backend_forkexec -- fork/exec off a backend process
- *
- * Some operating systems (WIN32) don't have fork() so we have to simulate
- * it by storing parameters that need to be passed to the child and
- * then create a new child process.
- *
- * returns the pid of the fork/exec'd process, or -1 on failure
- */
-static pid_t
-backend_forkexec(Port *port)
-{
-	char	   *av[4];
-	int			ac = 0;
-
-	av[ac++] = "postgres";
-	av[ac++] = "--forkbackend";
-	av[ac++] = NULL;			/* filled in by internal_forkexec */
-
-	av[ac] = NULL;
-	Assert(ac < lengthof(av));
-
-	return internal_forkexec(ac, av);
+	return internal_forkexec(fork_data->ac, fork_data->av);
 }
 
 #ifndef WIN32
@@ -5413,6 +5407,13 @@ StartChildProcess(ForkProcType type)
 			break;
 		case PgArchiverFork:
 			PrepPgArchiverFork(fork_data);
+			break;
+		case SysLoggerFork:
+			if (!Logging_collector)
+				return 0;
+
+			PrepSysLoggerFork(fork_data);
+			break;
 		case BgWorkerFork:
 			if (PrepBgWorkerFork(fork_data))
 			{
@@ -5422,11 +5423,8 @@ StartChildProcess(ForkProcType type)
 				return -1;
 			}
 			break;
-		case SysLoggerFork:
-			if (!Logging_collector)
-				return 0;
-
-			PrepSysLoggerFork(fork_data);
+		case BackendFork:
+			PrepBackendFork(fork_data);
 			break;
 		default:
 			break;
@@ -5439,7 +5437,7 @@ StartChildProcess(ForkProcType type)
 		return 0;
 
 #ifdef EXEC_BACKEND
-	pid = postmaster_forkexec(fork_data->ac, fork_data->av);
+	pid = postmaster_forkexec(fork_data);
 #else
 	pid = fork_process();
 #endif
@@ -5453,10 +5451,11 @@ StartChildProcess(ForkProcType type)
 		InitPostmasterChild();
 
 		/* Release postmaster's working memory context */
-		if (type != SysLoggerFork)
+		if (type != SysLoggerFork && type != BackendFork)
 		{
 			/* Close the postmaster's sockets */
 			ClosePostmasterPorts(false);
+
 			if (type == BgWorkerFork)
 			{
 				/*
@@ -5504,6 +5503,7 @@ StartChildProcess(ForkProcType type)
 				ExitPostmaster(1);
 				break;
 			case BgWorkerFork:
+			case BackendFork:
 				fork_data->fail_main(fork_data->ac, fork_data->av);
 				return -1;
 			default:
@@ -5518,6 +5518,7 @@ StartChildProcess(ForkProcType type)
 		{
 			case SysLoggerFork:
 			case BgWorkerFork:
+			case BackendFork:
 				fork_data->postmaster_main(fork_data->ac, fork_data->av);
 				break;
 			default:
diff --git a/src/include/postmaster/fork_process.h b/src/include/postmaster/fork_process.h
index b5d5457ce7..631a2113b5 100644
--- a/src/include/postmaster/fork_process.h
+++ b/src/include/postmaster/fork_process.h
@@ -30,6 +30,7 @@ typedef enum
    PgArchiverFork,
    SysLoggerFork,
    BgWorkerFork,
+   BackendFork,
 
    NUMFORKPROCTYPES			/* Must be last! */
 } ForkProcType;
@@ -53,4 +54,8 @@ typedef struct ForkProcData
 
 extern pid_t fork_process(void);
 
+#ifdef EXEC_BACKEND
+extern pid_t postmaster_forkexec(ForkProcData *fork_data);
+#endif
+
 #endif							/* FORK_PROCESS_H */
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 9962bdd4be..a52952b74e 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -92,7 +92,6 @@ extern bool PostmasterMarkPIDForWorkerNotify(int);
 extern bool RandomCancelKey(int32 *cancel_key);
 
 #ifdef EXEC_BACKEND
-extern pid_t postmaster_forkexec(int argc, char *argv[]);
 extern void SubPostmasterMain(int argc, char *argv[]) pg_attribute_noreturn();
 
 extern Size ShmemBackendArraySize(void);
-- 
2.23.0

