From e4e1f4360d44508e9d562cb32543cce0bb424a95 Mon Sep 17 00:00:00 2001
From: Mike Palmiotto <mike.palmiotto@crunchydata.com>
Date: Wed, 11 Mar 2020 03:32:41 +0000
Subject: [PATCH v2 08/12] Add BgWorkerType to subprocess struct

Export some of the backend-specific code and move the some bgworker
startup code out to src/backend/postmaster/bgworker.c.
---
 src/backend/postmaster/bgworker.c           | 165 ++++++++++-
 src/backend/postmaster/postmaster.c         | 294 +++-----------------
 src/backend/postmaster/subprocess.c         |  12 +
 src/backend/utils/init/globals.c            |   1 +
 src/include/miscadmin.h                     |   1 +
 src/include/postmaster/bgworker_internals.h |   8 +-
 src/include/postmaster/postmaster.h         |  53 ++++
 src/include/postmaster/subprocess.h         |   1 +
 8 files changed, 268 insertions(+), 267 deletions(-)

diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 6c684b5e12..6f62cd4889 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -33,8 +33,10 @@
 #include "storage/shmem.h"
 #include "tcop/tcopprot.h"
 #include "utils/ascii.h"
+#include "utils/memutils.h"
 #include "utils/ps_status.h"
 #include "utils/timeout.h"
+#include "utils/timestamp.h"
 
 /*
  * The postmaster's list of registered background workers, in private memory.
@@ -133,7 +135,55 @@ static const struct
 
 /* Private functions. */
 static bgworker_main_type LookupBackgroundWorkerFunction(const char *libraryname, const char *funcname);
+static bool assign_backendlist_entry(RegisteredBgWorker *rw);
 
+/*
+ * Allocate the Backend struct for a connected background worker, but don't
+ * add it to the list of backends just yet.
+ *
+ * On failure, return false without changing any worker state.
+ *
+ * Some info from the Backend is copied into the passed rw.
+ */
+static bool
+assign_backendlist_entry(RegisteredBgWorker *rw)
+{
+	Backend    *bn;
+
+	/*
+	 * Compute the cancel key that will be assigned to this session. We
+	 * probably don't need cancel keys for background workers, but we'd better
+	 * have something random in the field to prevent unfriendly people from
+	 * sending cancels to them.
+	 */
+	if (!RandomCancelKey(&MyCancelKey))
+	{
+		ereport(LOG,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("could not generate random cancel key")));
+		return false;
+	}
+
+	bn = malloc(sizeof(Backend));
+	if (bn == NULL)
+	{
+		ereport(LOG,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory")));
+		return false;
+	}
+
+	bn->cancel_key = MyCancelKey;
+	bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
+	bn->bkend_type = BACKEND_TYPE_BGWORKER;
+	bn->dead_end = false;
+	bn->bgworker_notify = false;
+
+	rw->rw_backend = bn;
+	rw->rw_child_slot = bn->child_slot;
+
+	return true;
+}
 
 /*
  * Calculate shared memory needed.
@@ -670,6 +720,55 @@ bgworker_sigusr1_handler(SIGNAL_ARGS)
 	errno = save_errno;
 }
 
+/*
+ *	BackgroundWorkerPrep
+ *
+ *  Postmaster subroutine to prepare a bgworker subprocess.
+ *
+ *	Returns 0 on success or -1 on failure.
+ *
+ *	Note: if fail, we will be called again from the postmaster main loop.
+ */
+int
+BackgroundWorkerPrep(int argc, char *argv[])
+{
+	RegisteredBgWorker *rw = MyRegisteredBgWorker;
+#ifdef EXEC_BACKEND
+	char				forkav[MAXPGPATH];
+#endif
+
+	Assert(MyRegisteredBgWorker);
+	Assert(rw->rw_pid == 0);
+
+	/*
+	 * Allocate and assign the Backend element.  Note we must do this before
+	 * forking, so that we can handle out of memory properly.
+	 *
+	 * Treat failure as though the worker had crashed.  That way, the
+	 * postmaster will wait a bit before attempting to start it again; if it
+	 * tried again right away, most likely it'd find itself repeating the
+	 * out-of-memory or fork failure condition.
+	 */
+	if (!assign_backendlist_entry(rw))
+	{
+		rw->rw_crashed_at = GetCurrentTimestamp();
+		return -1;
+	}
+
+	ereport(DEBUG1,
+			(errmsg("starting background worker process \"%s\"",
+					rw->rw_worker.bgw_name)));
+
+#ifdef EXEC_BACKEND
+	/* Rewrite bgworker fork parameter with shmem_slot */
+	snprintf(forkav, MAXPGPATH, "%s=%d", argv[1], rw->rw_shmem_slot);
+	argv[1] = forkav;
+#endif
+
+	return 0;
+}
+
+
 /*
  * Start a new background worker
  *
@@ -677,12 +776,28 @@ bgworker_sigusr1_handler(SIGNAL_ARGS)
  * postmaster.
  */
 void
-StartBackgroundWorker(void)
+BackgroundWorkerMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
 {
 	sigjmp_buf	local_sigjmp_buf;
-	BackgroundWorker *worker = MyBgworkerEntry;
+	BackgroundWorker *worker;
 	bgworker_main_type entrypt;
 
+#ifndef EXEC_BACKEND
+	/*
+	 * Before blowing away PostmasterContext, save this bgworker's
+	 * data where it can find it.
+	 */
+	MyBgworkerEntry = (BackgroundWorker *)
+						MemoryContextAlloc(TopMemoryContext, sizeof(BackgroundWorker));
+	memcpy(MyBgworkerEntry, &(MyRegisteredBgWorker->rw_worker), sizeof(BackgroundWorker));
+
+	/* Now we can release postmaster's working memory context */
+	MemoryContextSwitchTo(TopMemoryContext);
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+#endif
+
+	worker = MyBgworkerEntry;
 	if (worker == NULL)
 		elog(FATAL, "unable to find bgworker entry");
 
@@ -816,6 +931,52 @@ StartBackgroundWorker(void)
 	proc_exit(0);
 }
 
+/*
+ * BackgroundWorkerForkFailure
+ *
+ * Handle bgworker fork failure gracefully.
+ */
+bool
+BackgroundWorkerForkFailure(int fork_errno)
+{
+	RegisteredBgWorker *rw = MyRegisteredBgWorker;
+
+	errno = fork_errno;
+
+	/* in postmaster, fork failed ... */
+	ereport(LOG,
+			(errmsg("could not fork worker process: %m")));
+
+	/* undo what assign_backendlist_entry did */
+	ReleasePostmasterChildSlot(rw->rw_child_slot);
+	rw->rw_child_slot = 0;
+	free(rw->rw_backend);
+	rw->rw_backend = NULL;
+	/* mark entry as crashed, so we'll try again later */
+	rw->rw_crashed_at = GetCurrentTimestamp();
+
+	/* Don't panic. */
+	return false;
+}
+
+/*
+ * BackgroundWorkerPostmasterMain
+ */
+void
+BackgroundWorkerPostmasterMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
+{
+	RegisteredBgWorker *rw = MyRegisteredBgWorker;
+
+	rw->rw_pid = MyChildProcPid;
+	rw->rw_backend->pid = rw->rw_pid;
+	ReportBackgroundWorkerPID(rw);
+	/* add new worker to lists of backends */
+	dlist_push_head(&BackendList, &(rw->rw_backend->elem));
+#ifdef EXEC_BACKEND
+	ShmemBackendArrayAdd(rw->rw_backend);
+#endif
+}
+
 /*
  * Register a new static background worker.
  *
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index f724727b1e..8343a81923 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -137,60 +137,14 @@
 #include "storage/spin.h"
 #endif
 
-
-/*
- * Possible types of a backend. Beyond being the possible bkend_type values in
- * struct bkend, these are OR-able request flag bits for SignalSomeChildren()
- * and CountChildren().
- */
-#define BACKEND_TYPE_NORMAL		0x0001	/* normal backend */
-#define BACKEND_TYPE_AUTOVAC	0x0002	/* autovacuum worker process */
-#define BACKEND_TYPE_WALSND		0x0004	/* walsender process */
-#define BACKEND_TYPE_BGWORKER	0x0008	/* bgworker process */
-#define BACKEND_TYPE_ALL		0x000F	/* OR of all the above */
-
-#define BACKEND_TYPE_WORKER		(BACKEND_TYPE_AUTOVAC | BACKEND_TYPE_BGWORKER)
-
-/*
- * List of active backends (or child processes anyway; we don't actually
- * know whether a given child has become a backend or is still in the
- * authorization phase).  This is used mainly to keep track of how many
- * children we have and send them appropriate signals when necessary.
- *
- * "Special" children such as the startup, bgwriter and autovacuum launcher
- * tasks are not in this list.  Autovacuum worker and walsender are in it.
- * Also, "dead_end" children are in it: these are children launched just for
- * the purpose of sending a friendly rejection message to a would-be client.
- * We must track them because they are attached to shared memory, but we know
- * they will never become live backends.  dead_end children are not assigned a
- * PMChildSlot.
- *
- * Background workers are in this list, too.
- */
-typedef struct bkend
-{
-	pid_t		pid;			/* process id of backend */
-	int32		cancel_key;		/* cancel key for cancels for this backend */
-	int			child_slot;		/* PMChildSlot for this backend, if any */
-
-	/*
-	 * Flavor of backend or auxiliary process.  Note that BACKEND_TYPE_WALSND
-	 * backends initially announce themselves as BACKEND_TYPE_NORMAL, so if
-	 * bkend_type is normal, you should check for a recent transition.
-	 */
-	int			bkend_type;
-	bool		dead_end;		/* is it going to send an error and quit? */
-	bool		bgworker_notify;	/* gets bgworker start/stop notifications */
-	dlist_node	elem;			/* list link in BackendList */
-} Backend;
-
-static dlist_head BackendList = DLIST_STATIC_INIT(BackendList);
+dlist_head BackendList = DLIST_STATIC_INIT(BackendList);
 
 #ifdef EXEC_BACKEND
 static Backend *ShmemBackendArray;
 #endif
 
 BackgroundWorker *MyBgworkerEntry = NULL;
+RegisteredBgWorker *MyRegisteredBgWorker = NULL;
 
 /* Struct containing postmaster subprocess control info */
 SubprocessType		MySubprocessType;
@@ -412,7 +366,6 @@ static void processCancelRequest(Port *port, void *pkt);
 static int	initMasks(fd_set *rmask);
 static void report_fork_failure_to_client(Port *port, int errnum);
 static CAC_state canAcceptConnections(int backend_type);
-static bool RandomCancelKey(int32 *cancel_key);
 static void signal_child(pid_t pid, int signal);
 static bool SignalSomeChildren(int signal, int targets);
 static void TerminateChildren(int signal);
@@ -420,7 +373,6 @@ static void TerminateChildren(int signal);
 #define SignalChildren(sig)			   SignalSomeChildren(sig, BACKEND_TYPE_ALL)
 
 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 StartSubprocess(SubprocessType type);
@@ -539,10 +491,10 @@ static bool save_backend_variables(BackendParameters *param, Port *port,
 								   HANDLE childProcess, pid_t childPid);
 #endif
 
-static void ShmemBackendArrayAdd(Backend *bn);
 static void ShmemBackendArrayRemove(Backend *bn);
 #endif							/* EXEC_BACKEND */
 
+#define BGWORKER_LEN			15
 
 /* Macros to check exit status of a child process */
 #define EXIT_STATUS_0(st)  ((st) == 0)
@@ -4878,6 +4830,7 @@ void
 SubPostmasterMain(int argc, char *argv[])
 {
 	Port		port;
+	int			shmem_slot;
 
 	/* In EXEC_BACKEND case we will not have inherited these settings */
 	IsPostmasterEnvironment = true;
@@ -5041,59 +4994,48 @@ SubPostmasterMain(int argc, char *argv[])
 		/* And run the backend */
 		BackendRun(&port);		/* does not return */
 	}
-	if (strncmp(argv[1], "--forkbgworker=", 15) == 0)
-	{
-		int			shmem_slot;
-
-		/* do this as early as possible; in particular, before InitProcess() */
-		IsBackgroundWorker = true;
 
+	if (MySubprocess->needs_aux_proc)
+	{
 		/* Restore basic shared memory pointers */
 		InitShmemAccess(UsedShmemSegAddr);
 
 		/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
-		InitProcess();
+		InitAuxiliaryProcess();
 
 		/* Attach process to shared data structures */
 		CreateSharedMemoryAndSemaphores();
 
-		/* Fetch MyBgworkerEntry from shared memory */
-		shmem_slot = atoi(argv[1] + 15);
-		MyBgworkerEntry = BackgroundWorkerEntry(shmem_slot);
+		AuxiliaryProcessMain(argc - 2, argv + 2);	/* does not return */
+	}
 
-		StartBackgroundWorker();
+	if (MySubprocessType == BgWorkerType)
+	{
+		/* do this as early as possible; in particular, before InitProcess() */
+		IsBackgroundWorker = true;
 	}
 
-	if (MySubprocess->needs_aux_proc)
+	if (MySubprocess->needs_shmem)
 	{
 		/* Restore basic shared memory pointers */
 		InitShmemAccess(UsedShmemSegAddr);
 
 		/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
-		InitAuxiliaryProcess();
+		InitProcess();
 
 		/* Attach process to shared data structures */
 		CreateSharedMemoryAndSemaphores();
-
-		AuxiliaryProcessMain(argc - 2, argv + 2);	/* does not return */
 	}
-	else
-	{
-		if (MySubprocess->needs_shmem)
-		{
-			/* Restore basic shared memory pointers */
-			InitShmemAccess(UsedShmemSegAddr);
-
-			/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
-			InitProcess();
 
-			/* Attach process to shared data structures */
-			CreateSharedMemoryAndSemaphores();
-		}
-
-		MySubprocess->entrypoint(argc - 2, argv + 2);
+	if (MySubprocessType == BgWorkerType)
+	{
+		/* Fetch MyBgworkerEntry from shared memory */
+		shmem_slot = atoi(argv[1] + 15);
+		MyBgworkerEntry = BackgroundWorkerEntry(shmem_slot);
 	}
 
+	MySubprocess->entrypoint(argc - 2, argv + 2);
+
 	abort();					/* shouldn't get here */
 }
 #endif							/* EXEC_BACKEND */
@@ -5356,7 +5298,7 @@ StartupPacketTimeoutHandler(void)
 /*
  * Generate a random cancel key.
  */
-static bool
+bool
 RandomCancelKey(int32 *cancel_key)
 {
 	return pg_strong_random(cancel_key, sizeof(int32));
@@ -5454,15 +5396,17 @@ StartSubprocess(SubprocessType type)
 
 #ifdef EXEC_BACKEND
 	pid = postmaster_forkexec(argc, argv);
+	MyChildProcPid = pid;
 #else							/* !EXEC_BACKEND */
 	pid = fork_process();
+	MyChildProcPid = pid;
 
 	if (pid == 0)				/* child */
 	{
 		InitPostmasterChild();
 
 		/* Close the postmaster's sockets */
-		ClosePostmasterPorts(false);
+		ClosePostmasterPorts(MySubprocessType == SysLoggerType);
 
 		/*
 		 * Release postmaster's working memory context if the subprocess doesn't
@@ -5512,6 +5456,8 @@ StartSubprocess(SubprocessType type)
 		{
 			if (MySubprocess->fork_failure(save_errno))
 				ExitPostmaster(1);
+			else
+				return -1;
 		}
 
 		return 0;
@@ -5754,124 +5700,6 @@ BackgroundWorkerUnblockSignals(void)
 	PG_SETMASK(&UnBlockSig);
 }
 
-#ifdef EXEC_BACKEND
-static pid_t
-bgworker_forkexec(int shmem_slot)
-{
-	char	   *av[10];
-	int			ac = 0;
-	char		forkav[MAXPGPATH];
-
-	snprintf(forkav, MAXPGPATH, "--forkbgworker=%d", shmem_slot);
-
-	av[ac++] = "postgres";
-	av[ac++] = forkav;
-	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
-	av[ac] = NULL;
-
-	Assert(ac < lengthof(av));
-
-	return postmaster_forkexec(ac, av);
-}
-#endif
-
-/*
- * Start a new bgworker.
- * Starting time conditions must have been checked already.
- *
- * Returns true on success, false on failure.
- * In either case, update the RegisteredBgWorker's state appropriately.
- *
- * This code is heavily based on autovacuum.c, q.v.
- */
-static bool
-do_start_bgworker(RegisteredBgWorker *rw)
-{
-	pid_t		worker_pid;
-
-	Assert(rw->rw_pid == 0);
-
-	/*
-	 * Allocate and assign the Backend element.  Note we must do this before
-	 * forking, so that we can handle failures (out of memory or child-process
-	 * slots) cleanly.
-	 *
-	 * Treat failure as though the worker had crashed.  That way, the
-	 * postmaster will wait a bit before attempting to start it again; if we
-	 * tried again right away, most likely we'd find ourselves hitting the
-	 * same resource-exhaustion condition.
-	 */
-	if (!assign_backendlist_entry(rw))
-	{
-		rw->rw_crashed_at = GetCurrentTimestamp();
-		return false;
-	}
-
-	ereport(DEBUG1,
-			(errmsg("starting background worker process \"%s\"",
-					rw->rw_worker.bgw_name)));
-
-#ifdef EXEC_BACKEND
-	switch ((worker_pid = bgworker_forkexec(rw->rw_shmem_slot)))
-#else
-	switch ((worker_pid = fork_process()))
-#endif
-	{
-		case -1:
-			/* in postmaster, fork failed ... */
-			ereport(LOG,
-					(errmsg("could not fork worker process: %m")));
-			/* undo what assign_backendlist_entry did */
-			ReleasePostmasterChildSlot(rw->rw_child_slot);
-			rw->rw_child_slot = 0;
-			free(rw->rw_backend);
-			rw->rw_backend = NULL;
-			/* mark entry as crashed, so we'll try again later */
-			rw->rw_crashed_at = GetCurrentTimestamp();
-			break;
-
-#ifndef EXEC_BACKEND
-		case 0:
-			/* in postmaster child ... */
-			InitPostmasterChild();
-
-			/* Close the postmaster's sockets */
-			ClosePostmasterPorts(false);
-
-			/*
-			 * Before blowing away PostmasterContext, save this bgworker's
-			 * data where it can find it.
-			 */
-			MyBgworkerEntry = (BackgroundWorker *)
-				MemoryContextAlloc(TopMemoryContext, sizeof(BackgroundWorker));
-			memcpy(MyBgworkerEntry, &rw->rw_worker, sizeof(BackgroundWorker));
-
-			/* Release postmaster's working memory context */
-			MemoryContextSwitchTo(TopMemoryContext);
-			MemoryContextDelete(PostmasterContext);
-			PostmasterContext = NULL;
-
-			StartBackgroundWorker();
-
-			exit(1);			/* should not get here */
-			break;
-#endif
-		default:
-			/* in postmaster, fork successful ... */
-			rw->rw_pid = worker_pid;
-			rw->rw_backend->pid = rw->rw_pid;
-			ReportBackgroundWorkerPID(rw);
-			/* add new worker to lists of backends */
-			dlist_push_head(&BackendList, &rw->rw_backend->elem);
-#ifdef EXEC_BACKEND
-			ShmemBackendArrayAdd(rw->rw_backend);
-#endif
-			return true;
-	}
-
-	return false;
-}
-
 /*
  * Does the current postmaster state require starting a worker with the
  * specified start_time?
@@ -5912,67 +5740,6 @@ bgworker_should_start_now(BgWorkerStartTime start_time)
 	return false;
 }
 
-/*
- * Allocate the Backend struct for a connected background worker, but don't
- * add it to the list of backends just yet.
- *
- * On failure, return false without changing any worker state.
- *
- * Some info from the Backend is copied into the passed rw.
- */
-static bool
-assign_backendlist_entry(RegisteredBgWorker *rw)
-{
-	Backend    *bn;
-
-	/*
-	 * Check that database state allows another connection.  Currently the
-	 * only possible failure is CAC_TOOMANY, so we just log an error message
-	 * based on that rather than checking the error code precisely.
-	 */
-	if (canAcceptConnections(BACKEND_TYPE_BGWORKER) != CAC_OK)
-	{
-		ereport(LOG,
-				(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
-				 errmsg("no slot available for new worker process")));
-		return false;
-	}
-
-	/*
-	 * Compute the cancel key that will be assigned to this session. We
-	 * probably don't need cancel keys for background workers, but we'd better
-	 * have something random in the field to prevent unfriendly people from
-	 * sending cancels to them.
-	 */
-	if (!RandomCancelKey(&MyCancelKey))
-	{
-		ereport(LOG,
-				(errcode(ERRCODE_INTERNAL_ERROR),
-				 errmsg("could not generate random cancel key")));
-		return false;
-	}
-
-	bn = malloc(sizeof(Backend));
-	if (bn == NULL)
-	{
-		ereport(LOG,
-				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("out of memory")));
-		return false;
-	}
-
-	bn->cancel_key = MyCancelKey;
-	bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
-	bn->bkend_type = BACKEND_TYPE_BGWORKER;
-	bn->dead_end = false;
-	bn->bgworker_notify = false;
-
-	rw->rw_backend = bn;
-	rw->rw_child_slot = bn->child_slot;
-
-	return true;
-}
-
 /*
  * If the time is right, start background worker(s).
  *
@@ -6077,7 +5844,8 @@ maybe_start_bgworkers(void)
 			 * crashed, but there's no need because the next run of this
 			 * function will do that.
 			 */
-			if (!do_start_bgworker(rw))
+			MyRegisteredBgWorker = rw;
+			if (StartSubprocess(BgWorkerType) <= 0)
 			{
 				StartWorkerNeeded = true;
 				return;
@@ -6488,7 +6256,7 @@ ShmemBackendArrayAllocation(void)
 	memset(ShmemBackendArray, 0, size);
 }
 
-static void
+void
 ShmemBackendArrayAdd(Backend *bn)
 {
 	/* The array slot corresponding to my PMChildSlot should be free */
diff --git a/src/backend/postmaster/subprocess.c b/src/backend/postmaster/subprocess.c
index f9d07de4ae..d4df791aeb 100644
--- a/src/backend/postmaster/subprocess.c
+++ b/src/backend/postmaster/subprocess.c
@@ -14,6 +14,7 @@
 #include "bootstrap/bootstrap.h"
 #include "pgstat.h"
 #include "postmaster/autovacuum.h"
+#include "postmaster/bgworker_internals.h"
 #include "postmaster/bgwriter.h"
 #include "postmaster/pgarch.h"
 #include "postmaster/postmaster.h"
@@ -158,6 +159,17 @@ static PgSubprocess process_types[] = {
 		.entrypoint = SysLoggerMain,
 		.fork_failure = NULL,
 		.postmaster_main = SysLoggerPostmasterMain
+	},
+	{
+		.name = "bgworker",
+		.desc = "background worker",
+		.needs_aux_proc = false,
+		.needs_shmem = true,
+		.keep_postmaster_memcontext = true,
+		.fork_prep = BackgroundWorkerPrep,
+		.entrypoint = BackgroundWorkerMain,
+		.fork_failure = BackgroundWorkerForkFailure,
+		.postmaster_main = BackgroundWorkerPostmasterMain
 	}
 };
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index eb19644419..b31ce82f75 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -38,6 +38,7 @@ volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
 
 int			MyProcPid;
+int			MyChildProcPid;
 pg_time_t	MyStartTime;
 TimestampTz MyStartTimestamp;
 struct Port *MyProcPort;
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 14fa127ab1..541ca5145d 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -163,6 +163,7 @@ extern PGDLLIMPORT int max_worker_processes;
 extern PGDLLIMPORT int max_parallel_workers;
 
 extern PGDLLIMPORT int MyProcPid;
+extern PGDLLIMPORT int MyChildProcPid;
 extern PGDLLIMPORT pg_time_t MyStartTime;
 extern PGDLLIMPORT TimestampTz MyStartTimestamp;
 extern PGDLLIMPORT struct Port *MyProcPort;
diff --git a/src/include/postmaster/bgworker_internals.h b/src/include/postmaster/bgworker_internals.h
index f7e24664d5..874d5a203f 100644
--- a/src/include/postmaster/bgworker_internals.h
+++ b/src/include/postmaster/bgworker_internals.h
@@ -43,6 +43,7 @@ typedef struct RegisteredBgWorker
 } RegisteredBgWorker;
 
 extern slist_head BackgroundWorkerList;
+extern RegisteredBgWorker	*MyRegisteredBgWorker;
 
 extern Size BackgroundWorkerShmemSize(void);
 extern void BackgroundWorkerShmemInit(void);
@@ -53,8 +54,11 @@ extern void ReportBackgroundWorkerExit(slist_mutable_iter *cur);
 extern void BackgroundWorkerStopNotifications(pid_t pid);
 extern void ResetBackgroundWorkerCrashTimes(void);
 
-/* Function to start a background worker, called from postmaster.c */
-extern void StartBackgroundWorker(void) pg_attribute_noreturn();
+/* BgWorkerType subprocess */
+extern int BackgroundWorkerPrep(int argc, char *argv[]);
+extern void BackgroundWorkerMain(int argc, char *argv[]);
+extern bool BackgroundWorkerForkFailure(int fork_errno);
+extern void BackgroundWorkerPostmasterMain(int argc, char *argv[]);
 
 #ifdef EXEC_BACKEND
 extern BackgroundWorker *BackgroundWorkerEntry(int slotno);
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index babc87dfc9..b1411c1ed2 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -13,6 +13,41 @@
 #ifndef _POSTMASTER_H
 #define _POSTMASTER_H
 
+#include "lib/ilist.h"
+
+/*
+ * List of active backends (or child processes anyway; we don't actually
+ * know whether a given child has become a backend or is still in the
+ * authorization phase).  This is used mainly to keep track of how many
+ * children we have and send them appropriate signals when necessary.
+ *
+ * "Special" children such as the startup, bgwriter and autovacuum launcher
+ * tasks are not in this list.  Autovacuum worker and walsender are in it.
+ * Also, "dead_end" children are in it: these are children launched just for
+ * the purpose of sending a friendly rejection message to a would-be client.
+ * We must track them because they are attached to shared memory, but we know
+ * they will never become live backends.  dead_end children are not assigned a
+ * PMChildSlot.
+ *
+ * Background workers are in this list, too.
+ */
+typedef struct bkend
+{
+	pid_t		pid;			/* process id of backend */
+	int32		cancel_key;		/* cancel key for cancels for this backend */
+	int			child_slot;		/* PMChildSlot for this backend, if any */
+
+	/*
+	 * Flavor of backend or auxiliary process.  Note that BACKEND_TYPE_WALSND
+	 * backends initially announce themselves as BACKEND_TYPE_NORMAL, so if
+	 * bkend_type is normal, you should check for a recent transition.
+	 */
+	int			bkend_type;
+	bool		dead_end;		/* is it going to send an error and quit? */
+	bool		bgworker_notify;	/* gets bgworker start/stop notifications */
+	dlist_node	elem;			/* list link in BackendList */
+} Backend;
+
 /* GUC options */
 extern bool EnableSSL;
 extern int	ReservedBackends;
@@ -54,14 +89,19 @@ extern int	MaxLivePostmasterChildren(void);
 
 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);
 extern void ShmemBackendArrayAllocation(void);
+extern void ShmemBackendArrayAdd(Backend *bn);
 #endif
 
+extern PGDLLIMPORT dlist_head BackendList;
+
 /*
  * Note: MAX_BACKENDS is limited to 2^18-1 because that's the width reserved
  * for buffer references in buf_internals.h.  This limitation could be lifted
@@ -74,4 +114,17 @@ extern void ShmemBackendArrayAllocation(void);
  */
 #define MAX_BACKENDS	0x3FFFF
 
+/*
+ * Possible types of a backend. Beyond being the possible bkend_type values in
+ * struct bkend, these are OR-able request flag bits for SignalSomeChildren()
+ * and CountChildren().
+ */
+#define BACKEND_TYPE_NORMAL		0x0001	/* normal backend */
+#define BACKEND_TYPE_AUTOVAC	0x0002	/* autovacuum worker process */
+#define BACKEND_TYPE_WALSND		0x0004	/* walsender process */
+#define BACKEND_TYPE_BGWORKER	0x0008	/* bgworker process */
+#define BACKEND_TYPE_ALL		0x000F	/* OR of all the above */
+
+#define BACKEND_TYPE_WORKER		(BACKEND_TYPE_AUTOVAC | BACKEND_TYPE_BGWORKER)
+
 #endif							/* _POSTMASTER_H */
diff --git a/src/include/postmaster/subprocess.h b/src/include/postmaster/subprocess.h
index d0d98a5d69..706d60e4d6 100644
--- a/src/include/postmaster/subprocess.h
+++ b/src/include/postmaster/subprocess.h
@@ -28,6 +28,7 @@ typedef enum
 	PgstatCollectorType,
 	PgArchiverType,
 	SysLoggerType,
+	BgWorkerType,
 
 	NUMSUBPROCESSTYPES			/* Must be last! */
 } SubprocessType;
-- 
2.17.0

