From aa2c3209969fe4b6d193b37d92959dfd3809c804 Mon Sep 17 00:00:00 2001
From: Mike Palmiotto <mike.palmiotto@crunchydata.com>
Date: Mon, 16 Mar 2020 23:05:33 +0000
Subject: [PATCH v2 09/12] Add Backends to subprocess struct

---
 src/backend/postmaster/postmaster.c | 407 +++++++++++++++-------------
 src/backend/postmaster/subprocess.c |  11 +
 src/include/postmaster/postmaster.h |   6 +
 src/include/postmaster/subprocess.h |   1 +
 4 files changed, 235 insertions(+), 190 deletions(-)

diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 8343a81923..f979d505df 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -359,7 +359,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 +408,6 @@ typedef struct
 } win32_deadchild_waitinfo;
 #endif							/* WIN32 */
 
-static pid_t backend_forkexec(Port *port);
 static pid_t internal_forkexec(int argc, char *argv[], Port *port);
 
 /* Type for a socket that can be inherited to a client process */
@@ -512,6 +510,8 @@ int			postmaster_alive_fds[2] = {-1, -1};
 HANDLE		PostmasterHandle;
 #endif
 
+static Port *ConnProcPort = NULL;
+
 /*
  * Postmaster main entry point
  */
@@ -1671,19 +1671,18 @@ ServerLoop(void)
 					break;
 				if (FD_ISSET(ListenSocket[i], &rmask))
 				{
-					Port	   *port;
+					ConnProcPort = ConnCreate(ListenSocket[i]);
 
-					port = ConnCreate(ListenSocket[i]);
-					if (port)
+					if (ConnProcPort)
 					{
-						BackendStartup(port);
+						StartSubprocess(ClientBackendType);
 
 						/*
 						 * We no longer need the open socket or port structure
 						 * in this process
 						 */
-						StreamClose(port->sock);
-						ConnFree(port);
+						StreamClose(ConnProcPort->sock);
+						ConnFree(ConnProcPort);
 					}
 				}
 			}
@@ -3215,6 +3214,174 @@ reaper(SIGNAL_ARGS)
 	errno = save_errno;
 }
 
+/*
+ * BackendPrep
+ *
+ * Do all of the pre-fork setup for a new Backend.
+*/
+int
+BackendPrep(int argc, char *argv[])
+{
+	/*
+	 * Create backend data structure.  Better before the fork() so we can
+	 * handle failure cleanly.
+	 */
+	Backend *bn;
+
+	bn = (Backend *) malloc(sizeof(Backend));
+	if (!bn)
+	{
+		ereport(LOG,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory")));
+	}
+
+	/*
+	 * 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")));
+	}
+
+	bn->cancel_key = MyCancelKey;
+
+	/* Pass down canAcceptConnections state */
+	ConnProcPort->canAcceptConnections = canAcceptConnections(BACKEND_TYPE_NORMAL);
+	bn->dead_end = (ConnProcPort->canAcceptConnections != CAC_OK &&
+						   ConnProcPort->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;
+
+	/*
+	 * Push the Backend to the stack as-is so it can be retreived by the parent
+	 * on the other side of the fork/exec. They will modify this entry in place.
+	 */
+	dlist_push_head(&BackendList, &bn->elem);
+
+	return 0;
+}
+
+/*
+ *	 BackendMain
+ *
+ *	 Child code when forking a Backend.
+ */
+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.
+	 */
+#ifndef EXEC_BACKEND
+	BackendInitialize(ConnProcPort);
+#endif
+
+	/* And run the backend */
+	BackendRun(ConnProcPort);		/* does not return */
+}
+
+/*
+ *	 BackendPostmasterMain
+ *
+ *	 Parent code when forking a Backend.
+ */
+void BackendPostmasterMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
+{
+	dlist_mutable_iter iter;
+
+	/*
+	 * Find our backend node by popping the element with a matching child slot
+	 * off the list. This was pushed to the stack (incomplete) by BackendPrep.
+	 * We need to fix it up and push back.
+	 */
+	dlist_foreach_modify(iter, &BackendList)
+	{
+		Backend *bp = dlist_container(Backend, elem, iter.cur);
+
+		/* MyPMChildSlot is guaranteed to match the child MyPMChildSlot */
+		if (bp->child_slot == MyPMChildSlot)
+		{
+			/*
+			 * Everything's been successful, it's safe to add this backend to our list
+			 * of backends.
+			 */
+			bp->pid = MyChildProcPid;
+			bp->bkend_type = BACKEND_TYPE_NORMAL;	/* Can change later to WALSND */
+
+#ifdef EXEC_BACKEND
+			if (!bp->dead_end)
+				ShmemBackendArrayAdd(bp);
+#endif
+			break;	/* There is only one entry and we've found it */
+		}
+	}
+
+	/* in parent, successful fork */
+	ereport(DEBUG2,
+			(errmsg_internal("forked new backend, pid=%d socket=%d",
+							 (int) MyChildProcPid, (int) MyProcPort->sock)));
+}
+
+/*
+ *	 BackendForkFailure
+ *
+ *	 Backend cleanup in case a failure occurs forking a new Backend.
+ */
+bool
+BackendForkFailure(int child_errno)
+{
+	dlist_mutable_iter iter;
+
+	dlist_foreach_modify(iter, &BackendList)
+	{
+		Backend    *bp = dlist_container(Backend, elem, iter.cur);
+
+		if (bp->child_slot == MyPMChildSlot)
+		{
+			if (!bp->dead_end)
+			{
+				if (!ReleasePostmasterChildSlot(bp->child_slot))
+				{
+					/* Could not release child slot! Panic! */
+					return true;
+				}
+#ifdef EXEC_BACKEND
+				ShmemBackendArrayRemove(bp);
+#endif
+			}
+			dlist_delete(iter.cur);
+			free(bp);
+			break;
+		}
+	}
+
+	report_fork_failure_to_client(MyProcPort, child_errno);
+
+	/* Don't panic */
+	return false;
+}
+
+
 /*
  * Scan the bgworkers list and see if the given PID (which has just stopped
  * or crashed) is in it.  Handle its shutdown if so, and return true.  If not a
@@ -4075,122 +4242,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(BACKEND_TYPE_NORMAL);
-	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
@@ -4483,33 +4534,12 @@ 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);
-}
-
-/*
- * 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));
+	if (!ConnProcPort)
+		memset(&port, 0, sizeof(port));
+	else
+		port = *ConnProcPort;
 
-	return internal_forkexec(ac, av, port);
+	return internal_forkexec(argc, argv, &port);
 }
 
 #ifndef WIN32
@@ -4849,6 +4879,7 @@ SubPostmasterMain(int argc, char *argv[])
 	/* 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);
@@ -4890,11 +4921,7 @@ SubPostmasterMain(int argc, char *argv[])
 	 * sometimes impossible to attach to shared memory at the desired address.
 	 * Return the setting to its old value (usually '1' or '2') when finished.
 	 */
-	if (strcmp(argv[1], "--forkbackend") == 0 ||
-		strcmp(argv[1], "--forkavlauncher") == 0 ||
-		strcmp(argv[1], "--forkavworker") == 0 ||
-		strcmp(argv[1], "--forkboot") == 0 ||
-		strncmp(argv[1], "--forkbgworker=", 15) == 0)
+	if (MySubprocess->needs_shmem)
 		PGSharedMemoryReAttach();
 	else
 		PGSharedMemoryNoReAttach();
@@ -4943,11 +4970,23 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	process_shared_preload_libraries();
 
-	/* Run backend or appropriate child */
-	if (strcmp(argv[1], "--forkbackend") == 0)
+	if (MySubprocess->needs_aux_proc)
 	{
-		Assert(argc == 3);		/* shouldn't be any more args */
+		/* Restore basic shared memory pointers */
+		InitShmemAccess(UsedShmemSegAddr);
 
+		/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
+		InitAuxiliaryProcess();
+
+		/* Attach process to shared data structures */
+		CreateSharedMemoryAndSemaphores();
+
+		AuxiliaryProcessMain(argc - 2, argv + 2);	/* does not return */
+	}
+
+	/* Run backend or appropriate child */
+	if (MySubprocessType == ClientBackendType)
+	{
 		/*
 		 * Need to reinitialize the SSL library in the backend, since the
 		 * context structures contain function pointers and cannot be passed
@@ -4981,32 +5020,6 @@ SubPostmasterMain(int argc, char *argv[])
 		 * report the error to the client.
 		 */
 		BackendInitialize(&port);
-
-		/* Restore basic shared memory pointers */
-		InitShmemAccess(UsedShmemSegAddr);
-
-		/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
-		InitProcess();
-
-		/* Attach process to shared data structures */
-		CreateSharedMemoryAndSemaphores();
-
-		/* And run the backend */
-		BackendRun(&port);		/* does not return */
-	}
-
-	if (MySubprocess->needs_aux_proc)
-	{
-		/* Restore basic shared memory pointers */
-		InitShmemAccess(UsedShmemSegAddr);
-
-		/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
-		InitAuxiliaryProcess();
-
-		/* Attach process to shared data structures */
-		CreateSharedMemoryAndSemaphores();
-
-		AuxiliaryProcessMain(argc - 2, argv + 2);	/* does not return */
 	}
 
 	if (MySubprocessType == BgWorkerType)
@@ -5027,15 +5040,29 @@ SubPostmasterMain(int argc, char *argv[])
 		CreateSharedMemoryAndSemaphores();
 	}
 
-	if (MySubprocessType == BgWorkerType)
+	switch (MySubprocessType)
 	{
-		/* Fetch MyBgworkerEntry from shared memory */
-		shmem_slot = atoi(argv[1] + 15);
-		MyBgworkerEntry = BackgroundWorkerEntry(shmem_slot);
+		case AutoVacuumLauncherType:
+		case AutoVacuumWorkerType:
+			MySubprocess->entrypoint(argc - 2, argv + 2);
+			break;
+		case BgWorkerType:
+			/* Fetch MyBgworkerEntry from shared memory */
+			shmem_slot = atoi(argv[1] + 15);
+			MyBgworkerEntry = BackgroundWorkerEntry(shmem_slot);
+			/* fallthrough */
+		case PgArchiverType:
+		case PgstatCollectorType:
+		case SysLoggerType:
+		case ClientBackendType:
+			MySubprocess->entrypoint(argc, argv);
+			break;
+		default:
+			ereport(LOG,
+					(errmsg("could not start unknown process type (%d) under postmaster",
+							MySubprocessType)));
 	}
 
-	MySubprocess->entrypoint(argc - 2, argv + 2);
-
 	abort();					/* shouldn't get here */
 }
 #endif							/* EXEC_BACKEND */
diff --git a/src/backend/postmaster/subprocess.c b/src/backend/postmaster/subprocess.c
index d4df791aeb..9d7ce86918 100644
--- a/src/backend/postmaster/subprocess.c
+++ b/src/backend/postmaster/subprocess.c
@@ -170,6 +170,17 @@ static PgSubprocess process_types[] = {
 		.entrypoint = BackgroundWorkerMain,
 		.fork_failure = BackgroundWorkerForkFailure,
 		.postmaster_main = BackgroundWorkerPostmasterMain
+	},
+	{
+		.name = "backend",
+		.desc = "backend",
+		.needs_aux_proc = false,
+		.needs_shmem = true,
+		.keep_postmaster_memcontext = true,
+		.fork_prep = BackendPrep,
+		.entrypoint = BackendMain,
+		.fork_failure = BackendForkFailure,
+		.postmaster_main = BackendPostmasterMain
 	}
 };
 
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index b1411c1ed2..6bc0af7c2b 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -91,6 +91,12 @@ extern bool PostmasterMarkPIDForWorkerNotify(int);
 
 extern bool RandomCancelKey(int32 *cancel_key);
 
+extern int BackendPrep(int argc, char *argv[]);
+extern void BackendMain(int argc, char *argv[]);
+extern void BackendPostmasterMain(int argc, char *argv[]);
+extern bool BackendForkFailure(int child_errno);
+extern void BackgroundWorkerMain(int argc, char *argv[]);
+
 #ifdef EXEC_BACKEND
 extern pid_t postmaster_forkexec(int argc, char *argv[]);
 extern void SubPostmasterMain(int argc, char *argv[]) pg_attribute_noreturn();
diff --git a/src/include/postmaster/subprocess.h b/src/include/postmaster/subprocess.h
index 706d60e4d6..06e67efecc 100644
--- a/src/include/postmaster/subprocess.h
+++ b/src/include/postmaster/subprocess.h
@@ -29,6 +29,7 @@ typedef enum
 	PgArchiverType,
 	SysLoggerType,
 	BgWorkerType,
+	ClientBackendType,
 
 	NUMSUBPROCESSTYPES			/* Must be last! */
 } SubprocessType;
-- 
2.17.0

