public inbox for [email protected]
help / color / mirror / Atom feedFrom: Heikki Linnakangas <[email protected]>
To: pgsql-hackers <[email protected]>
Subject: Refactoring backend fork+exec code
Date: Sun, 18 Jun 2023 14:22:33 +0300
Message-ID: <[email protected]> (raw)
I started to look at the code in postmaster.c related to launching child
processes. I tried to reduce the difference between EXEC_BACKEND and
!EXEC_BACKEND code paths, and put the code that needs to differ behind a
better abstraction. I started doing this to help with implementing
multi-threading, but it doesn't introduce anything thread-related yet
and I think this improves readability anyway.
This is still work-inprogress, especially the last, big, patch in the
patch set. Mainly, I need to clean up the comments in the new
launch_backend.c file. But the other patches are in pretty good shape,
and if you ignore launch_backend.c, you can see the effect on the other
source files.
With these patches, there is a new function for launching a postmaster
child process:
pid_t postmaster_child_launch(PostmasterChildType child_type, char
*startup_data, size_t startup_data_len, ClientSocket *client_sock);
This function hides the differences between EXEC_BACKEND and
!EXEC_BACKEND cases.
In 'startup_data', the caller can pass a blob of data to the child
process, with different meaning for different kinds of child processes.
For a backend process, for example, it's used to pass the CAC_state,
which indicates whether the backend accepts the connection or just sends
"too many clients" error. And for background workers, it's used to pass
the BackgroundWorker struct. The startup data is passed to the child
process in the
ClientSocket is a new struct holds a socket FD, and the local and remote
address info. Before this patch set, postmaster initializes the Port
structs but only fills in those fields in it. With this patch set, we
have a new ClientSocket struct just for those fields, which makes it
more clear which fields are initialized where.
I haven't done much testing yet, and no testing at all on Windows, so
that's probably still broken.
--
Heikki Linnakangas
Neon (https://neon.tech)
Attachments:
[text/x-patch] 0001-Allocate-Backend-structs-in-PostmasterContext.patch (4.9K, ../[email protected]/2-0001-Allocate-Backend-structs-in-PostmasterContext.patch)
download | inline diff:
From 89cbf93c3490d38e7add149fd168f4f7a7eb670e Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Sun, 18 Jun 2023 10:41:29 +0300
Subject: [PATCH 1/9] Allocate Backend structs in PostmasterContext.
The child processes don't need them. By allocating them in
PostmasterContext, the memory gets free'd and is made available for
other stuff in the child processes.
---
src/backend/postmaster/bgworker.c | 11 ++++++++---
src/backend/postmaster/postmaster.c | 24 +++++++++++-------------
2 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 0dd22b23511..07ac68f50bc 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -33,6 +33,7 @@
#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"
@@ -344,7 +345,9 @@ BackgroundWorkerStateChange(bool allow_new_workers)
/*
* Copy the registration data into the registered workers list.
*/
- rw = malloc(sizeof(RegisteredBgWorker));
+ rw = MemoryContextAllocExtended(PostmasterContext,
+ sizeof(RegisteredBgWorker),
+ MCXT_ALLOC_NO_OOM);
if (rw == NULL)
{
ereport(LOG,
@@ -452,7 +455,7 @@ ForgetBackgroundWorker(slist_mutable_iter *cur)
rw->rw_worker.bgw_name)));
slist_delete_current(cur);
- free(rw);
+ pfree(rw);
}
/*
@@ -926,7 +929,9 @@ RegisterBackgroundWorker(BackgroundWorker *worker)
/*
* Copy the registration data into the registered workers list.
*/
- rw = malloc(sizeof(RegisteredBgWorker));
+ rw = MemoryContextAllocExtended(PostmasterContext,
+ sizeof(RegisteredBgWorker),
+ MCXT_ALLOC_NO_OOM);
if (rw == NULL)
{
ereport(LOG,
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 4c49393fc5a..ee9e24e4e74 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -3357,7 +3357,7 @@ CleanupBackgroundWorker(int pid,
*/
if (rw->rw_backend->bgworker_notify)
BackgroundWorkerStopNotifications(rw->rw_pid);
- free(rw->rw_backend);
+ pfree(rw->rw_backend);
rw->rw_backend = NULL;
rw->rw_pid = 0;
rw->rw_child_slot = 0;
@@ -3450,7 +3450,7 @@ CleanupBackend(int pid,
BackgroundWorkerStopNotifications(bp->pid);
}
dlist_delete(iter.cur);
- free(bp);
+ pfree(bp);
break;
}
}
@@ -3506,7 +3506,7 @@ HandleChildCrash(int pid, int exitstatus, const char *procname)
#ifdef EXEC_BACKEND
ShmemBackendArrayRemove(rw->rw_backend);
#endif
- free(rw->rw_backend);
+ pfree(rw->rw_backend);
rw->rw_backend = NULL;
rw->rw_pid = 0;
rw->rw_child_slot = 0;
@@ -3543,7 +3543,7 @@ HandleChildCrash(int pid, int exitstatus, const char *procname)
#endif
}
dlist_delete(iter.cur);
- free(bp);
+ pfree(bp);
/* Keep looping so we can signal remaining backends */
}
else
@@ -4119,7 +4119,7 @@ BackendStartup(Port *port)
* Create backend data structure. Better before the fork() so we can
* handle failure cleanly.
*/
- bn = (Backend *) malloc(sizeof(Backend));
+ bn = (Backend *) palloc_extended(sizeof(Backend), MCXT_ALLOC_NO_OOM);
if (!bn)
{
ereport(LOG,
@@ -4135,7 +4135,7 @@ BackendStartup(Port *port)
*/
if (!RandomCancelKey(&MyCancelKey))
{
- free(bn);
+ pfree(bn);
ereport(LOG,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("could not generate random cancel key")));
@@ -4165,8 +4165,6 @@ BackendStartup(Port *port)
pid = fork_process();
if (pid == 0) /* child */
{
- free(bn);
-
/* Detangle from postmaster */
InitPostmasterChild();
@@ -4197,7 +4195,7 @@ BackendStartup(Port *port)
if (!bn->dead_end)
(void) ReleasePostmasterChildSlot(bn->child_slot);
- free(bn);
+ pfree(bn);
errno = save_errno;
ereport(LOG,
(errmsg("could not fork new process for connection: %m")));
@@ -5460,7 +5458,7 @@ StartAutovacuumWorker(void)
return;
}
- bn = (Backend *) malloc(sizeof(Backend));
+ bn = (Backend *) palloc_extended(sizeof(Backend), MCXT_ALLOC_NO_OOM);
if (bn)
{
bn->cancel_key = MyCancelKey;
@@ -5487,7 +5485,7 @@ StartAutovacuumWorker(void)
* logged by StartAutoVacWorker
*/
(void) ReleasePostmasterChildSlot(bn->child_slot);
- free(bn);
+ pfree(bn);
}
else
ereport(LOG,
@@ -5732,7 +5730,7 @@ do_start_bgworker(RegisteredBgWorker *rw)
/* undo what assign_backendlist_entry did */
ReleasePostmasterChildSlot(rw->rw_child_slot);
rw->rw_child_slot = 0;
- free(rw->rw_backend);
+ pfree(rw->rw_backend);
rw->rw_backend = NULL;
/* mark entry as crashed, so we'll try again later */
rw->rw_crashed_at = GetCurrentTimestamp();
@@ -5858,7 +5856,7 @@ assign_backendlist_entry(RegisteredBgWorker *rw)
return false;
}
- bn = malloc(sizeof(Backend));
+ bn = palloc_extended(sizeof(Backend), MCXT_ALLOC_NO_OOM);
if (bn == NULL)
{
ereport(LOG,
--
2.30.2
[text/x-patch] 0002-Pass-background-worker-entry-in-the-parameter-file.patch (5.5K, ../[email protected]/3-0002-Pass-background-worker-entry-in-the-parameter-file.patch)
download | inline diff:
From 0a99a16741977ef2b10ebee8198692e818f8b700 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Sun, 18 Jun 2023 12:45:50 +0300
Subject: [PATCH 2/9] Pass background worker entry in the parameter file
This makes it possible to move InitProcess later in SubPostmasterMain
(in next commit), as we don't need an lwlock and hence a PGPROC entry
to get the background worker entry anymore.
---
src/backend/postmaster/bgworker.c | 21 ------------
src/backend/postmaster/postmaster.c | 37 ++++++++++++++-------
src/include/postmaster/bgworker_internals.h | 4 ---
3 files changed, 25 insertions(+), 37 deletions(-)
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 07ac68f50bc..b7140306e43 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -628,27 +628,6 @@ ResetBackgroundWorkerCrashTimes(void)
}
}
-#ifdef EXEC_BACKEND
-/*
- * In EXEC_BACKEND mode, workers use this to retrieve their details from
- * shared memory.
- */
-BackgroundWorker *
-BackgroundWorkerEntry(int slotno)
-{
- static BackgroundWorker myEntry;
- BackgroundWorkerSlot *slot;
-
- Assert(slotno < BackgroundWorkerData->total_slots);
- slot = &BackgroundWorkerData->slot[slotno];
- Assert(slot->in_use);
-
- /* must copy this in case we don't intend to retain shmem access */
- memcpy(&myEntry, &slot->worker, sizeof myEntry);
- return &myEntry;
-}
-#endif
-
/*
* Complain about the BackgroundWorker definition using error level elevel.
* Return true if it looks ok, false if not (unless elevel >= ERROR, in
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index ee9e24e4e74..a7a9e061c5b 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -540,6 +540,8 @@ typedef struct
#endif
char my_exec_path[MAXPGPATH];
char pkglib_path[MAXPGPATH];
+
+ BackgroundWorker MyBgworkerEntry;
} BackendParameters;
static void read_backend_variables(char *id, Port *port);
@@ -4867,7 +4869,7 @@ SubPostmasterMain(int argc, char *argv[])
strcmp(argv[1], "--forkavlauncher") == 0 ||
strcmp(argv[1], "--forkavworker") == 0 ||
strcmp(argv[1], "--forkaux") == 0 ||
- strncmp(argv[1], "--forkbgworker=", 15) == 0)
+ strncmp(argv[1], "--forkbgworker", 14) == 0)
PGSharedMemoryReAttach();
else
PGSharedMemoryNoReAttach();
@@ -4998,10 +5000,8 @@ SubPostmasterMain(int argc, char *argv[])
AutoVacWorkerMain(argc - 2, argv + 2); /* does not return */
}
- if (strncmp(argv[1], "--forkbgworker=", 15) == 0)
+ if (strncmp(argv[1], "--forkbgworker", 14) == 0)
{
- int shmem_slot;
-
/* do this as early as possible; in particular, before InitProcess() */
IsBackgroundWorker = true;
@@ -5014,10 +5014,6 @@ SubPostmasterMain(int argc, char *argv[])
/* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores();
- /* Fetch MyBgworkerEntry from shared memory */
- shmem_slot = atoi(argv[1] + 15);
- MyBgworkerEntry = BackgroundWorkerEntry(shmem_slot);
-
StartBackgroundWorker();
}
if (strcmp(argv[1], "--forklog") == 0)
@@ -5662,13 +5658,14 @@ BackgroundWorkerUnblockSignals(void)
#ifdef EXEC_BACKEND
static pid_t
-bgworker_forkexec(int shmem_slot)
+bgworker_forkexec(BackgroundWorker *worker)
{
char *av[10];
int ac = 0;
char forkav[MAXPGPATH];
+ pid_t result;
- snprintf(forkav, MAXPGPATH, "--forkbgworker=%d", shmem_slot);
+ snprintf(forkav, MAXPGPATH, "--forkbgworker");
av[ac++] = "postgres";
av[ac++] = forkav;
@@ -5677,7 +5674,11 @@ bgworker_forkexec(int shmem_slot)
Assert(ac < lengthof(av));
- return postmaster_forkexec(ac, av);
+ MyBgworkerEntry = worker;
+ result = postmaster_forkexec(ac, av);
+ MyBgworkerEntry = NULL;
+
+ return result;
}
#endif
@@ -5718,7 +5719,7 @@ do_start_bgworker(RegisteredBgWorker *rw)
rw->rw_worker.bgw_name)));
#ifdef EXEC_BACKEND
- switch ((worker_pid = bgworker_forkexec(rw->rw_shmem_slot)))
+ switch ((worker_pid = bgworker_forkexec(&rw->rw_worker)))
#else
switch ((worker_pid = fork_process()))
#endif
@@ -6120,6 +6121,11 @@ save_backend_variables(BackendParameters *param, Port *port,
strlcpy(param->pkglib_path, pkglib_path, MAXPGPATH);
+ if (MyBgworkerEntry)
+ memcpy(¶m->MyBgworkerEntry, MyBgworkerEntry, sizeof(BackgroundWorker));
+ else
+ memset(¶m->MyBgworkerEntry, 0, sizeof(BackgroundWorker));
+
return true;
}
@@ -6350,6 +6356,13 @@ restore_backend_variables(BackendParameters *param, Port *port)
strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
+ if (param->MyBgworkerEntry.bgw_name[0] != '\0')
+ {
+ MyBgworkerEntry = (BackgroundWorker *)
+ MemoryContextAlloc(TopMemoryContext, sizeof(BackgroundWorker));
+ memcpy(MyBgworkerEntry, ¶m->MyBgworkerEntry, sizeof(BackgroundWorker));
+ }
+
/*
* We need to restore fd.c's counts of externally-opened FDs; to avoid
* confusion, be sure to do this after restoring max_safe_fds. (Note:
diff --git a/src/include/postmaster/bgworker_internals.h b/src/include/postmaster/bgworker_internals.h
index 4ad63fd9bd7..7fa7ee2d878 100644
--- a/src/include/postmaster/bgworker_internals.h
+++ b/src/include/postmaster/bgworker_internals.h
@@ -57,8 +57,4 @@ extern void ResetBackgroundWorkerCrashTimes(void);
/* Function to start a background worker, called from postmaster.c */
extern void StartBackgroundWorker(void) pg_attribute_noreturn();
-#ifdef EXEC_BACKEND
-extern BackgroundWorker *BackgroundWorkerEntry(int slotno);
-#endif
-
#endif /* BGWORKER_INTERNALS_H */
--
2.30.2
[text/x-patch] 0003-Refactor-CreateSharedMemoryAndSemaphores.patch (9.1K, ../[email protected]/4-0003-Refactor-CreateSharedMemoryAndSemaphores.patch)
download | inline diff:
From 0cb6f8d665980d30a5d2a29013000744f16bf813 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Sun, 18 Jun 2023 11:00:21 +0300
Subject: [PATCH 3/9] Refactor CreateSharedMemoryAndSemaphores.
Moves InitProcess calls a little later in EXEC_BACKEND case.
For clarity, have separate functions for *creating* the shared memory
and semaphores, at postmaster or single-user backend startup, and for
*attaching* to existing shared memory structures in EXEC_BACKEND case.
I find it pretty confusing in all the *ShmemInit() functions too, that
they are called in two different contexts: in postmaster when creating
the shmem structs, and in the child process in EXEC_BACKEND when
attaching to the already existing structs. But this commit doesn't
change that.
---
src/backend/postmaster/autovacuum.c | 22 ++++++-------
src/backend/postmaster/auxprocess.c | 8 ++---
src/backend/postmaster/bgworker.c | 11 +++----
src/backend/postmaster/postmaster.c | 48 ++++++-----------------------
src/backend/storage/ipc/ipci.c | 11 +++++++
src/backend/storage/lmgr/proc.c | 2 +-
src/include/storage/ipc.h | 1 +
7 files changed, 41 insertions(+), 62 deletions(-)
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index f929b62e8ad..7157c5466aa 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -476,14 +476,13 @@ AutoVacLauncherMain(int argc, char *argv[])
pqsignal(SIGCHLD, SIG_DFL);
/*
- * Create a per-backend PGPROC struct in shared memory, except in the
- * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
- * this before we can use LWLocks (and in the EXEC_BACKEND case we already
- * had to do some stuff with LWLocks).
+ * Create a per-backend PGPROC struct in shared memory. We must do
+ * this before we can use LWLocks.
*/
-#ifndef EXEC_BACKEND
InitProcess();
-#endif
+
+ /* Attach process to shared data structures */
+ AttachSharedMemoryAndSemaphores();
/* Early initialization */
BaseInit();
@@ -1548,14 +1547,13 @@ AutoVacWorkerMain(int argc, char *argv[])
pqsignal(SIGCHLD, SIG_DFL);
/*
- * Create a per-backend PGPROC struct in shared memory, except in the
- * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
- * this before we can use LWLocks (and in the EXEC_BACKEND case we already
- * had to do some stuff with LWLocks).
+ * Create a per-backend PGPROC struct in shared memory. We must do
+ * this before we can use LWLocks.
*/
-#ifndef EXEC_BACKEND
InitProcess();
-#endif
+
+ /* Attach process to shared data structures */
+ AttachSharedMemoryAndSemaphores();
/* Early initialization */
BaseInit();
diff --git a/src/backend/postmaster/auxprocess.c b/src/backend/postmaster/auxprocess.c
index cae6feb3562..536d9a2b3e4 100644
--- a/src/backend/postmaster/auxprocess.c
+++ b/src/backend/postmaster/auxprocess.c
@@ -97,12 +97,12 @@ AuxiliaryProcessMain(AuxProcType auxtype)
*/
/*
- * Create a PGPROC so we can use LWLocks. In the EXEC_BACKEND case, this
- * was already done by SubPostmasterMain().
+ * Create a PGPROC so we can use LWLocks.
*/
-#ifndef EXEC_BACKEND
InitAuxiliaryProcess();
-#endif
+
+ /* Attach process to shared data structures */
+ AttachSharedMemoryAndSemaphores();
BaseInit();
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index b7140306e43..13519ea0c45 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -810,14 +810,13 @@ StartBackgroundWorker(void)
PG_exception_stack = &local_sigjmp_buf;
/*
- * Create a per-backend PGPROC struct in shared memory, except in the
- * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
- * this before we can use LWLocks (and in the EXEC_BACKEND case we already
- * had to do some stuff with LWLocks).
+ * Create a per-backend PGPROC struct in shared memory. We must do
+ * this before we can use LWLocks.
*/
-#ifndef EXEC_BACKEND
InitProcess();
-#endif
+
+ /* Attach process to shared data structures */
+ AttachSharedMemoryAndSemaphores();
/*
* Early initialization.
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index a7a9e061c5b..bf9f0d27278 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -4176,15 +4176,6 @@ BackendStartup(Port *port)
/* Perform additional initialization and collect startup packet */
BackendInitialize(port);
- /*
- * Create a per-backend PGPROC struct in shared memory. We must do
- * this before we can use LWLocks. In the !EXEC_BACKEND case (here)
- * this could be delayed a bit further, but EXEC_BACKEND needs to do
- * stuff with LWLocks before PostgresMain(), so we do it here as well
- * for symmetry.
- */
- InitProcess();
-
/* And run the backend */
BackendRun(port);
}
@@ -4452,6 +4443,15 @@ BackendInitialize(Port *port)
static void
BackendRun(Port *port)
{
+ /*
+ * Create a per-backend PGPROC struct in shared memory. We must do
+ * this before we can use LWLocks (in AttachSharedMemoryAndSemaphores).
+ */
+ InitProcess();
+
+ /* Attach process to shared data structures */
+ AttachSharedMemoryAndSemaphores();
+
/*
* Make sure we aren't in PostmasterContext anymore. (We can't delete it
* just yet, though, because InitPostgres will need the HBA data.)
@@ -4947,12 +4947,6 @@ SubPostmasterMain(int argc, char *argv[])
/* 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 */
}
@@ -4965,12 +4959,6 @@ SubPostmasterMain(int argc, char *argv[])
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
- /* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
- InitAuxiliaryProcess();
-
- /* Attach process to shared data structures */
- CreateSharedMemoryAndSemaphores();
-
auxtype = atoi(argv[3]);
AuxiliaryProcessMain(auxtype); /* does not return */
}
@@ -4979,12 +4967,6 @@ SubPostmasterMain(int argc, char *argv[])
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
- /* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
- InitProcess();
-
- /* Attach process to shared data structures */
- CreateSharedMemoryAndSemaphores();
-
AutoVacLauncherMain(argc - 2, argv + 2); /* does not return */
}
if (strcmp(argv[1], "--forkavworker") == 0)
@@ -4992,12 +4974,6 @@ SubPostmasterMain(int argc, char *argv[])
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
- /* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
- InitProcess();
-
- /* Attach process to shared data structures */
- CreateSharedMemoryAndSemaphores();
-
AutoVacWorkerMain(argc - 2, argv + 2); /* does not return */
}
if (strncmp(argv[1], "--forkbgworker", 14) == 0)
@@ -5008,12 +4984,6 @@ SubPostmasterMain(int argc, char *argv[])
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
- /* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
- InitProcess();
-
- /* Attach process to shared data structures */
- CreateSharedMemoryAndSemaphores();
-
StartBackgroundWorker();
}
if (strcmp(argv[1], "--forklog") == 0)
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 8f1ded7338f..be6a774e351 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -170,6 +170,17 @@ CalculateShmemSize(int *num_semaphores)
* check IsUnderPostmaster, rather than EXEC_BACKEND, to detect this case.
* This is a bit code-wasteful and could be cleaned up.)
*/
+void
+AttachSharedMemoryAndSemaphores(void)
+{
+ /* InitProcess must've been called already */
+
+ /* Init !EXEC_BACKEND mode, we inherited everything through the fork */
+#ifdef EXEC_BACKEND
+ CreateSharedMemoryAndSemaphores();
+#endif
+}
+
void
CreateSharedMemoryAndSemaphores(void)
{
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index dac921219fa..91f415aa783 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -468,7 +468,7 @@ InitProcess(void)
*
* This is separate from InitProcess because we can't acquire LWLocks until
* we've created a PGPROC, but in the EXEC_BACKEND case ProcArrayAdd won't
- * work until after we've done CreateSharedMemoryAndSemaphores.
+ * work until after we've done AttachSharedMemoryAndSemaphores.
*/
void
InitProcessPhase2(void)
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 888c08b3067..e75656f5242 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -79,6 +79,7 @@ extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
extern Size CalculateShmemSize(int *num_semaphores);
extern void CreateSharedMemoryAndSemaphores(void);
+extern void AttachSharedMemoryAndSemaphores(void);
extern void InitializeShmemGUCs(void);
#endif /* IPC_H */
--
2.30.2
[text/x-patch] 0004-Use-FD_CLOEXEC-on-ListenSockets.patch (3.0K, ../[email protected]/5-0004-Use-FD_CLOEXEC-on-ListenSockets.patch)
download | inline diff:
From 1d89eec53c7fefa7a4a8c011c9f19e3df64dc436 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Mon, 12 Jun 2023 16:33:20 +0300
Subject: [PATCH 4/9] Use FD_CLOEXEC on ListenSockets
We went through some effort to close them in the child process. Better to
not hand them down to the child process in the first place.
---
src/backend/libpq/pqcomm.c | 6 +++++-
src/backend/postmaster/postmaster.c | 14 ++++++--------
2 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index da5bb5fc5d3..9061daccc29 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -458,6 +458,9 @@ StreamServerPort(int family, const char *hostName, unsigned short portNumber,
}
#ifndef WIN32
+ /* Don't give the listen socket to any subprograms we execute. */
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
+ elog(FATAL, "fcntl(F_SETFD) failed on socket: %m");
/*
* Without the SO_REUSEADDR flag, a new postmaster can't be started
@@ -831,7 +834,8 @@ StreamConnection(pgsocket server_fd, Port *port)
void
StreamClose(pgsocket sock)
{
- closesocket(sock);
+ if (closesocket(sock) != 0)
+ elog(LOG, "closesocket failed: %m");
}
/*
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index bf9f0d27278..228744ebee5 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -497,7 +497,6 @@ typedef struct
Port port;
InheritableSocket portsocket;
char DataDir[MAXPGPATH];
- pgsocket ListenSocket[MAXLISTEN];
int32 MyCancelKey;
int MyPMChildSlot;
#ifndef WIN32
@@ -2575,8 +2574,6 @@ ConnFree(Port *port)
void
ClosePostmasterPorts(bool am_syslogger)
{
- int i;
-
/* Release resources held by the postmaster's WaitEventSet. */
if (pm_wait_set)
{
@@ -2603,8 +2600,12 @@ ClosePostmasterPorts(bool am_syslogger)
/*
* Close the postmaster's listen sockets. These aren't tracked by fd.c,
* so we don't call ReleaseExternalFD() here.
+ *
+ * The listen sockets are marked as FD_CLOEXEC, so this isn't needed in
+ * EXEC_BACKEND mode.
*/
- for (i = 0; i < MAXLISTEN; i++)
+#ifndef EXEC_BACKEND
+ for (int i = 0; i < MAXLISTEN; i++)
{
if (ListenSocket[i] != PGINVALID_SOCKET)
{
@@ -2612,6 +2613,7 @@ ClosePostmasterPorts(bool am_syslogger)
ListenSocket[i] = PGINVALID_SOCKET;
}
}
+#endif
/*
* If using syslogger, close the read side of the pipe. We don't bother
@@ -6035,8 +6037,6 @@ save_backend_variables(BackendParameters *param, Port *port,
strlcpy(param->DataDir, DataDir, MAXPGPATH);
- memcpy(¶m->ListenSocket, &ListenSocket, sizeof(ListenSocket));
-
param->MyCancelKey = MyCancelKey;
param->MyPMChildSlot = MyPMChildSlot;
@@ -6273,8 +6273,6 @@ restore_backend_variables(BackendParameters *param, Port *port)
SetDataDir(param->DataDir);
- memcpy(&ListenSocket, ¶m->ListenSocket, sizeof(ListenSocket));
-
MyCancelKey = param->MyCancelKey;
MyPMChildSlot = param->MyPMChildSlot;
--
2.30.2
[text/x-patch] 0005-Move-too-many-clients-already-et-al.-checks-from-Pro.patch (3.6K, ../[email protected]/6-0005-Move-too-many-clients-already-et-al.-checks-from-Pro.patch)
download | inline diff:
From 2f518be9e96cfed1a1a49b4af8f7cb4a837aa784 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Mon, 12 Jun 2023 18:07:54 +0300
Subject: [PATCH 5/9] Move "too many clients already" et al. checks from
ProcessStartupPacket.
The check is not about processing the startup packet, so the calling
function seems like a more natural place.
---
src/backend/postmaster/postmaster.c | 86 ++++++++++++++---------------
1 file changed, 43 insertions(+), 43 deletions(-)
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 228744ebee5..fa6f2c8e29c 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -2321,49 +2321,6 @@ retry1:
*/
MemoryContextSwitchTo(oldcontext);
- /*
- * If we're going to reject the connection due to database state, say so
- * now instead of wasting cycles on an authentication exchange. (This also
- * allows a pg_ping utility to be written.)
- */
- switch (port->canAcceptConnections)
- {
- case CAC_STARTUP:
- ereport(FATAL,
- (errcode(ERRCODE_CANNOT_CONNECT_NOW),
- errmsg("the database system is starting up")));
- break;
- case CAC_NOTCONSISTENT:
- if (EnableHotStandby)
- ereport(FATAL,
- (errcode(ERRCODE_CANNOT_CONNECT_NOW),
- errmsg("the database system is not yet accepting connections"),
- errdetail("Consistent recovery state has not been yet reached.")));
- else
- ereport(FATAL,
- (errcode(ERRCODE_CANNOT_CONNECT_NOW),
- errmsg("the database system is not accepting connections"),
- errdetail("Hot standby mode is disabled.")));
- break;
- case CAC_SHUTDOWN:
- ereport(FATAL,
- (errcode(ERRCODE_CANNOT_CONNECT_NOW),
- errmsg("the database system is shutting down")));
- break;
- case CAC_RECOVERY:
- ereport(FATAL,
- (errcode(ERRCODE_CANNOT_CONNECT_NOW),
- errmsg("the database system is in recovery mode")));
- break;
- case CAC_TOOMANY:
- ereport(FATAL,
- (errcode(ERRCODE_TOO_MANY_CONNECTIONS),
- errmsg("sorry, too many clients already")));
- break;
- case CAC_OK:
- break;
- }
-
return STATUS_OK;
}
@@ -4391,6 +4348,49 @@ BackendInitialize(Port *port)
*/
status = ProcessStartupPacket(port, false, false);
+ /*
+ * If we're going to reject the connection due to database state, say so
+ * now instead of wasting cycles on an authentication exchange. (This also
+ * allows a pg_ping utility to be written.)
+ */
+ switch (port->canAcceptConnections)
+ {
+ case CAC_STARTUP:
+ ereport(FATAL,
+ (errcode(ERRCODE_CANNOT_CONNECT_NOW),
+ errmsg("the database system is starting up")));
+ break;
+ case CAC_NOTCONSISTENT:
+ if (EnableHotStandby)
+ ereport(FATAL,
+ (errcode(ERRCODE_CANNOT_CONNECT_NOW),
+ errmsg("the database system is not yet accepting connections"),
+ errdetail("Consistent recovery state has not been yet reached.")));
+ else
+ ereport(FATAL,
+ (errcode(ERRCODE_CANNOT_CONNECT_NOW),
+ errmsg("the database system is not accepting connections"),
+ errdetail("Hot standby mode is disabled.")));
+ break;
+ case CAC_SHUTDOWN:
+ ereport(FATAL,
+ (errcode(ERRCODE_CANNOT_CONNECT_NOW),
+ errmsg("the database system is shutting down")));
+ break;
+ case CAC_RECOVERY:
+ ereport(FATAL,
+ (errcode(ERRCODE_CANNOT_CONNECT_NOW),
+ errmsg("the database system is in recovery mode")));
+ break;
+ case CAC_TOOMANY:
+ ereport(FATAL,
+ (errcode(ERRCODE_TOO_MANY_CONNECTIONS),
+ errmsg("sorry, too many clients already")));
+ break;
+ case CAC_OK:
+ break;
+ }
+
/*
* Disable the timeout, and prevent SIGTERM again.
*/
--
2.30.2
[text/x-patch] 0006-Pass-CAC-as-argument-to-backend-process.patch (5.3K, ../[email protected]/7-0006-Pass-CAC-as-argument-to-backend-process.patch)
download | inline diff:
From c25b67c045018a2bf05e6ff53819d26e561fc83f Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Sun, 18 Jun 2023 14:11:16 +0300
Subject: [PATCH 6/9] Pass CAC as argument to backend process.
We used to smuggle it to the child process in the Port struct, but it seems
better to pass it down as a separate argument.
---
src/backend/postmaster/postmaster.c | 43 +++++++++++++++++++++--------
src/include/libpq/libpq-be.h | 12 --------
2 files changed, 31 insertions(+), 24 deletions(-)
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index fa6f2c8e29c..3ce9d76a850 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -417,7 +417,18 @@ static void HandleChildCrash(int pid, int exitstatus, const char *procname);
static void LogChildExit(int lev, const char *procname,
int pid, int exitstatus);
static void PostmasterStateMachine(void);
-static void BackendInitialize(Port *port);
+
+typedef enum CAC_state
+{
+ CAC_OK,
+ CAC_STARTUP,
+ CAC_SHUTDOWN,
+ CAC_RECOVERY,
+ CAC_NOTCONSISTENT,
+ CAC_TOOMANY
+} CAC_state;
+
+static void BackendInitialize(Port *port, CAC_state cac);
static void BackendRun(Port *port) pg_attribute_noreturn();
static void ExitPostmaster(int status) pg_attribute_noreturn();
static int ServerLoop(void);
@@ -474,7 +485,7 @@ typedef struct
} win32_deadchild_waitinfo;
#endif /* WIN32 */
-static pid_t backend_forkexec(Port *port);
+static pid_t backend_forkexec(Port *port, CAC_state cac);
static pid_t internal_forkexec(int argc, char *argv[], Port *port);
/* Type for a socket that can be inherited to a client process */
@@ -4075,6 +4086,7 @@ BackendStartup(Port *port)
{
Backend *bn; /* for backend cleanup */
pid_t pid;
+ CAC_state cac;
/*
* Create backend data structure. Better before the fork() so we can
@@ -4106,8 +4118,8 @@ BackendStartup(Port *port)
bn->cancel_key = MyCancelKey;
/* Pass down canAcceptConnections state */
- port->canAcceptConnections = canAcceptConnections(BACKEND_TYPE_NORMAL);
- bn->dead_end = (port->canAcceptConnections != CAC_OK);
+ cac = canAcceptConnections(BACKEND_TYPE_NORMAL);
+ bn->dead_end = (cac != CAC_OK);
/*
* Unless it's a dead_end child, assign it a child slot number
@@ -4121,7 +4133,7 @@ BackendStartup(Port *port)
bn->bgworker_notify = false;
#ifdef EXEC_BACKEND
- pid = backend_forkexec(port);
+ pid = backend_forkexec(port, cac);
#else /* !EXEC_BACKEND */
pid = fork_process();
if (pid == 0) /* child */
@@ -4133,7 +4145,7 @@ BackendStartup(Port *port)
ClosePostmasterPorts(false);
/* Perform additional initialization and collect startup packet */
- BackendInitialize(port);
+ BackendInitialize(port, cac);
/* And run the backend */
BackendRun(port);
@@ -4220,7 +4232,7 @@ report_fork_failure_to_client(Port *port, int errnum)
* but have not yet set up most of our local pointers to shmem structures.
*/
static void
-BackendInitialize(Port *port)
+BackendInitialize(Port *port, CAC_state cac)
{
int status;
int ret;
@@ -4353,7 +4365,7 @@ BackendInitialize(Port *port)
* now instead of wasting cycles on an authentication exchange. (This also
* allows a pg_ping utility to be written.)
*/
- switch (port->canAcceptConnections)
+ switch (cac)
{
case CAC_STARTUP:
ereport(FATAL,
@@ -4498,15 +4510,19 @@ postmaster_forkexec(int argc, char *argv[])
* returns the pid of the fork/exec'd process, or -1 on failure
*/
static pid_t
-backend_forkexec(Port *port)
+backend_forkexec(Port *port, CAC_state cac)
{
- char *av[4];
+ char *av[5];
int ac = 0;
+ char cacbuf[10];
av[ac++] = "postgres";
av[ac++] = "--forkbackend";
av[ac++] = NULL; /* filled in by internal_forkexec */
+ snprintf(cacbuf, sizeof(cacbuf), "%d", (int) cac);
+ av[ac++] = cacbuf;
+
av[ac] = NULL;
Assert(ac < lengthof(av));
@@ -4910,7 +4926,10 @@ SubPostmasterMain(int argc, char *argv[])
/* Run backend or appropriate child */
if (strcmp(argv[1], "--forkbackend") == 0)
{
- Assert(argc == 3); /* shouldn't be any more args */
+ CAC_state cac;
+
+ Assert(argc == 4);
+ cac = (CAC_state) atoi(argv[3]);
/*
* Need to reinitialize the SSL library in the backend, since the
@@ -4944,7 +4963,7 @@ SubPostmasterMain(int argc, char *argv[])
* PGPROC slots, we have already initialized libpq and are able to
* report the error to the client.
*/
- BackendInitialize(&port);
+ BackendInitialize(&port, cac);
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 3b2ce9908f8..625caa853a0 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -58,17 +58,6 @@ typedef struct
#include "libpq/pqcomm.h"
-typedef enum CAC_state
-{
- CAC_OK,
- CAC_STARTUP,
- CAC_SHUTDOWN,
- CAC_RECOVERY,
- CAC_NOTCONSISTENT,
- CAC_TOOMANY
-} CAC_state;
-
-
/*
* GSSAPI specific state information
*/
@@ -156,7 +145,6 @@ typedef struct Port
int remote_hostname_resolv; /* see above */
int remote_hostname_errcode; /* see above */
char *remote_port; /* text rep of remote port */
- CAC_state canAcceptConnections; /* postmaster connection status */
/*
* Information that needs to be saved from the startup packet and passed
--
2.30.2
[text/x-patch] 0007-Remove-ConnCreate-and-ConnFree-and-allocate-Port-in-.patch (2.8K, ../[email protected]/8-0007-Remove-ConnCreate-and-ConnFree-and-allocate-Port-in-.patch)
download | inline diff:
From 658cba5cdb2e5c45faff84566906d2fcaa8a3674 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Mon, 12 Jun 2023 18:03:03 +0300
Subject: [PATCH 7/9] Remove ConnCreate and ConnFree, and allocate Port in
stack.
By allocating Port in stack, we don't need to deal with out-of-memory
errors, which makes the code a little less verbose.
---
src/backend/postmaster/postmaster.c | 68 +++++------------------------
1 file changed, 10 insertions(+), 58 deletions(-)
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 3ce9d76a850..8731b50e4a2 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -398,8 +398,6 @@ static void CloseServerPorts(int status, Datum arg);
static void unlink_external_pid_file(int status, Datum arg);
static void getInstallationPaths(const char *argv0);
static void checkControlFile(void);
-static Port *ConnCreate(int serverFd);
-static void ConnFree(Port *port);
static void handle_pm_pmsignal_signal(SIGNAL_ARGS);
static void handle_pm_child_exit_signal(SIGNAL_ARGS);
static void handle_pm_reload_request_signal(SIGNAL_ARGS);
@@ -1783,20 +1781,18 @@ ServerLoop(void)
if (events[i].events & WL_SOCKET_ACCEPT)
{
- Port *port;
+ Port port;
- port = ConnCreate(events[i].fd);
- if (port)
- {
- BackendStartup(port);
+ memset(&port, 0, sizeof(port));
+ if (StreamConnection(events[i].fd, &port) == STATUS_OK)
+ BackendStartup(&port);
- /*
- * We no longer need the open socket or port structure in
- * this process
- */
- StreamClose(port->sock);
- ConnFree(port);
- }
+ /*
+ * We no longer need the open socket or port structure in this
+ * process
+ */
+ if (port.sock != PGINVALID_SOCKET)
+ StreamClose(port.sock);
}
}
@@ -2485,50 +2481,6 @@ canAcceptConnections(int backend_type)
return result;
}
-
-/*
- * ConnCreate -- create a local connection data structure
- *
- * Returns NULL on failure, other than out-of-memory which is fatal.
- */
-static Port *
-ConnCreate(int serverFd)
-{
- Port *port;
-
- if (!(port = (Port *) calloc(1, sizeof(Port))))
- {
- ereport(LOG,
- (errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("out of memory")));
- ExitPostmaster(1);
- }
-
- if (StreamConnection(serverFd, port) != STATUS_OK)
- {
- if (port->sock != PGINVALID_SOCKET)
- StreamClose(port->sock);
- ConnFree(port);
- return NULL;
- }
-
- return port;
-}
-
-
-/*
- * ConnFree -- free a local connection data structure
- *
- * Caller has already closed the socket if any, so there's not much
- * to do here.
- */
-static void
-ConnFree(Port *port)
-{
- free(port);
-}
-
-
/*
* ClosePostmasterPorts -- close all the postmaster's open sockets
*
--
2.30.2
[text/x-patch] 0008-Introduce-ClientSocket-rename-some-funcs.patch (25.2K, ../[email protected]/9-0008-Introduce-ClientSocket-rename-some-funcs.patch)
download | inline diff:
From 65384b9a6cfb3b9b589041526216e0f64d64bea5 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Sun, 18 Jun 2023 13:56:44 +0300
Subject: [PATCH 8/9] Introduce ClientSocket, rename some funcs
- Move more of the work on a client socket to the child process.
- Reduce the amount of data that needs to be passed from postmaster to
child. (Used to pass a full Port struct, although most of the fields were
empty. Now we pass the much slimmer ClientSocket.)
---
src/backend/libpq/pqcomm.c | 93 ++++++++++---------
src/backend/postmaster/autovacuum.c | 8 +-
src/backend/postmaster/bgworker.c | 4 +-
src/backend/postmaster/postmaster.c | 139 +++++++++++++++-------------
src/include/libpq/libpq-be.h | 19 ++--
src/include/libpq/libpq.h | 6 +-
6 files changed, 147 insertions(+), 122 deletions(-)
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 9061daccc29..e2789cb4a4f 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -29,9 +29,9 @@
* INTERFACE ROUTINES
*
* setup/teardown:
- * StreamServerPort - Open postmaster's server port
- * StreamConnection - Create new connection with client
- * StreamClose - Close a client/backend connection
+ * ListenServerPort - Open postmaster's server port
+ * AcceptClientConnection - Create new connection with client
+ * StreamConnection - Initialize a client connection
* TouchSocketFiles - Protect socket files against /tmp cleaners
* pq_init - initialize libpq at backend startup
* socket_comm_reset - reset libpq during error recovery
@@ -304,7 +304,7 @@ socket_close(int code, Datum arg)
/*
- * StreamServerPort -- open a "listening" port to accept connections.
+ * ListenServerPort -- open a "listening" port to accept connections.
*
* family should be AF_UNIX or AF_UNSPEC; portNumber is the port number.
* For AF_UNIX ports, hostName should be NULL and unixSocketDir must be
@@ -318,7 +318,7 @@ socket_close(int code, Datum arg)
*/
int
-StreamServerPort(int family, const char *hostName, unsigned short portNumber,
+ListenServerPort(int family, const char *hostName, unsigned short portNumber,
const char *unixSocketDir,
pgsocket ListenSocket[], int MaxListen)
{
@@ -689,8 +689,9 @@ Setup_AF_UNIX(const char *sock_path)
/*
- * StreamConnection -- create a new connection with client using
- * server port. Set port->sock to the FD of the new connection.
+ * AcceptClientConnection -- accept a new connection with client using
+ * server port. Fills *client_sock with the FD and endpoint info
+ * of the new connection.
*
* ASSUME: that this doesn't need to be non-blocking because
* the Postmaster waits for the socket to be ready to accept().
@@ -698,13 +699,13 @@ Setup_AF_UNIX(const char *sock_path)
* RETURNS: STATUS_OK or STATUS_ERROR
*/
int
-StreamConnection(pgsocket server_fd, Port *port)
+AcceptClientConnection(pgsocket server_fd, ClientSocket *client_sock)
{
/* accept connection and fill in the client (remote) address */
- port->raddr.salen = sizeof(port->raddr.addr);
- if ((port->sock = accept(server_fd,
- (struct sockaddr *) &port->raddr.addr,
- &port->raddr.salen)) == PGINVALID_SOCKET)
+ client_sock->raddr.salen = sizeof(client_sock->raddr.addr);
+ if ((client_sock->sock = accept(server_fd,
+ (struct sockaddr *) &client_sock->raddr.addr,
+ &client_sock->raddr.salen)) == PGINVALID_SOCKET)
{
ereport(LOG,
(errcode_for_socket_access(),
@@ -722,10 +723,10 @@ StreamConnection(pgsocket server_fd, Port *port)
}
/* fill in the server (local) address */
- port->laddr.salen = sizeof(port->laddr.addr);
- if (getsockname(port->sock,
- (struct sockaddr *) &port->laddr.addr,
- &port->laddr.salen) < 0)
+ client_sock->laddr.salen = sizeof(client_sock->laddr.addr);
+ if (getsockname(client_sock->sock,
+ (struct sockaddr *) &client_sock->laddr.addr,
+ &client_sock->laddr.salen) < 0)
{
ereport(LOG,
(errmsg("%s() failed: %m", "getsockname")));
@@ -733,7 +734,7 @@ StreamConnection(pgsocket server_fd, Port *port)
}
/* select NODELAY and KEEPALIVE options if it's a TCP connection */
- if (port->laddr.addr.ss_family != AF_UNIX)
+ if (client_sock->laddr.addr.ss_family != AF_UNIX)
{
int on;
#ifdef WIN32
@@ -744,7 +745,7 @@ StreamConnection(pgsocket server_fd, Port *port)
#ifdef TCP_NODELAY
on = 1;
- if (setsockopt(port->sock, IPPROTO_TCP, TCP_NODELAY,
+ if (setsockopt(client_sock->sock, IPPROTO_TCP, TCP_NODELAY,
(char *) &on, sizeof(on)) < 0)
{
ereport(LOG,
@@ -753,7 +754,7 @@ StreamConnection(pgsocket server_fd, Port *port)
}
#endif
on = 1;
- if (setsockopt(port->sock, SOL_SOCKET, SO_KEEPALIVE,
+ if (setsockopt(client_sock->sock, SOL_SOCKET, SO_KEEPALIVE,
(char *) &on, sizeof(on)) < 0)
{
ereport(LOG,
@@ -785,7 +786,7 @@ StreamConnection(pgsocket server_fd, Port *port)
* https://msdn.microsoft.com/en-us/library/bb736549%28v=vs.85%29.aspx
*/
optlen = sizeof(oldopt);
- if (getsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &oldopt,
+ if (getsockopt(client_sock->sock, SOL_SOCKET, SO_SNDBUF, (char *) &oldopt,
&optlen) < 0)
{
ereport(LOG,
@@ -795,7 +796,7 @@ StreamConnection(pgsocket server_fd, Port *port)
newopt = PQ_SEND_BUFFER_SIZE * 4;
if (oldopt < newopt)
{
- if (setsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &newopt,
+ if (setsockopt(client_sock->sock, SOL_SOCKET, SO_SNDBUF, (char *) &newopt,
sizeof(newopt)) < 0)
{
ereport(LOG,
@@ -804,13 +805,34 @@ StreamConnection(pgsocket server_fd, Port *port)
}
}
#endif
+ }
+ return STATUS_OK;
+}
+
+/*
+ * StreamConnection -- create a new connection from the given socket.
+ *
+ * This runs in the backend process.
+ */
+Port *
+StreamConnection(ClientSocket *client_sock)
+{
+ Port *port;
+
+ port = palloc0(sizeof(Port));
+ port->sock = client_sock->sock;
+ port->laddr = client_sock->laddr;
+ port->raddr = client_sock->raddr;
+ /* Apply the current keepalive parameters if it's a TCP connection */
+ if (port->laddr.addr.ss_family != AF_UNIX)
+ {
/*
- * Also apply the current keepalive parameters. If we fail to set a
- * parameter, don't error out, because these aren't universally
- * supported. (Note: you might think we need to reset the GUC
- * variables to 0 in such a case, but it's not necessary because the
- * show hooks for these variables report the truth anyway.)
+ * If we fail to set a parameter, don't error out, because these
+ * aren't universally supported. (Note: you might think we need to
+ * reset the GUC variables to 0 in such a case, but it's not necessary
+ * because the show hooks for these variables report the truth
+ * anyway.)
*/
(void) pq_setkeepalivesidle(tcp_keepalives_idle, port);
(void) pq_setkeepalivesinterval(tcp_keepalives_interval, port);
@@ -818,24 +840,7 @@ StreamConnection(pgsocket server_fd, Port *port)
(void) pq_settcpusertimeout(tcp_user_timeout, port);
}
- return STATUS_OK;
-}
-
-/*
- * StreamClose -- close a client/backend connection
- *
- * NOTE: this is NOT used to terminate a session; it is just used to release
- * the file descriptor in a process that should no longer have the socket
- * open. (For example, the postmaster calls this after passing ownership
- * of the connection to a child process.) It is expected that someone else
- * still has the socket open. So, we only want to close the descriptor,
- * we do NOT want to send anything to the far end.
- */
-void
-StreamClose(pgsocket sock)
-{
- if (closesocket(sock) != 0)
- elog(LOG, "closesocket failed: %m");
+ return port;
}
/*
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 7157c5466aa..1041376951e 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -476,8 +476,8 @@ AutoVacLauncherMain(int argc, char *argv[])
pqsignal(SIGCHLD, SIG_DFL);
/*
- * Create a per-backend PGPROC struct in shared memory. We must do
- * this before we can use LWLocks.
+ * Create a per-backend PGPROC struct in shared memory. We must do this
+ * before we can use LWLocks.
*/
InitProcess();
@@ -1547,8 +1547,8 @@ AutoVacWorkerMain(int argc, char *argv[])
pqsignal(SIGCHLD, SIG_DFL);
/*
- * Create a per-backend PGPROC struct in shared memory. We must do
- * this before we can use LWLocks.
+ * Create a per-backend PGPROC struct in shared memory. We must do this
+ * before we can use LWLocks.
*/
InitProcess();
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 13519ea0c45..4c446ffd087 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -810,8 +810,8 @@ StartBackgroundWorker(void)
PG_exception_stack = &local_sigjmp_buf;
/*
- * Create a per-backend PGPROC struct in shared memory. We must do
- * this before we can use LWLocks.
+ * Create a per-backend PGPROC struct in shared memory. We must do this
+ * before we can use LWLocks.
*/
InitProcess();
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 8731b50e4a2..b73ba983f3e 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -426,15 +426,15 @@ typedef enum CAC_state
CAC_TOOMANY
} CAC_state;
-static void BackendInitialize(Port *port, CAC_state cac);
-static void BackendRun(Port *port) pg_attribute_noreturn();
+static void BackendInitialize(ClientSocket *client_sock, CAC_state cac);
+static void BackendRun(void) pg_attribute_noreturn();
static void ExitPostmaster(int status) pg_attribute_noreturn();
static int ServerLoop(void);
-static int BackendStartup(Port *port);
+static int BackendStartup(ClientSocket *port);
static int ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done);
static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options);
static void processCancelRequest(Port *port, void *pkt);
-static void report_fork_failure_to_client(Port *port, int errnum);
+static void report_fork_failure_to_client(ClientSocket *client_sock, int errnum);
static CAC_state canAcceptConnections(int backend_type);
static bool RandomCancelKey(int32 *cancel_key);
static void signal_child(pid_t pid, int signal);
@@ -483,8 +483,8 @@ typedef struct
} win32_deadchild_waitinfo;
#endif /* WIN32 */
-static pid_t backend_forkexec(Port *port, CAC_state cac);
-static pid_t internal_forkexec(int argc, char *argv[], Port *port);
+static pid_t backend_forkexec(ClientSocket *client_sock, CAC_state cac);
+static pid_t internal_forkexec(int argc, char *argv[], ClientSocket *client_sock);
/* Type for a socket that can be inherited to a client process */
#ifdef WIN32
@@ -503,8 +503,8 @@ typedef int InheritableSocket;
*/
typedef struct
{
- Port port;
- InheritableSocket portsocket;
+ ClientSocket client_sock;
+ InheritableSocket serialized_sock;
char DataDir[MAXPGPATH];
int32 MyCancelKey;
int MyPMChildSlot;
@@ -552,13 +552,13 @@ typedef struct
BackgroundWorker MyBgworkerEntry;
} BackendParameters;
-static void read_backend_variables(char *id, Port *port);
-static void restore_backend_variables(BackendParameters *param, Port *port);
+static void read_backend_variables(char *id, ClientSocket *client_sock);
+static void restore_backend_variables(BackendParameters *param, ClientSocket *client_sock);
#ifndef WIN32
-static bool save_backend_variables(BackendParameters *param, Port *port);
+static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock);
#else
-static bool save_backend_variables(BackendParameters *param, Port *port,
+static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock,
HANDLE childProcess, pid_t childPid);
#endif
@@ -1221,12 +1221,12 @@ PostmasterMain(int argc, char *argv[])
char *curhost = (char *) lfirst(l);
if (strcmp(curhost, "*") == 0)
- status = StreamServerPort(AF_UNSPEC, NULL,
+ status = ListenServerPort(AF_UNSPEC, NULL,
(unsigned short) PostPortNumber,
NULL,
ListenSocket, MAXLISTEN);
else
- status = StreamServerPort(AF_UNSPEC, curhost,
+ status = ListenServerPort(AF_UNSPEC, curhost,
(unsigned short) PostPortNumber,
NULL,
ListenSocket, MAXLISTEN);
@@ -1318,7 +1318,7 @@ PostmasterMain(int argc, char *argv[])
{
char *socketdir = (char *) lfirst(l);
- status = StreamServerPort(AF_UNIX, NULL,
+ status = ListenServerPort(AF_UNIX, NULL,
(unsigned short) PostPortNumber,
socketdir,
ListenSocket, MAXLISTEN);
@@ -1499,7 +1499,7 @@ CloseServerPorts(int status, Datum arg)
{
if (ListenSocket[i] != PGINVALID_SOCKET)
{
- StreamClose(ListenSocket[i]);
+ closesocket(ListenSocket[i]);
ListenSocket[i] = PGINVALID_SOCKET;
}
}
@@ -1781,18 +1781,20 @@ ServerLoop(void)
if (events[i].events & WL_SOCKET_ACCEPT)
{
- Port port;
+ ClientSocket s;
- memset(&port, 0, sizeof(port));
- if (StreamConnection(events[i].fd, &port) == STATUS_OK)
- BackendStartup(&port);
+ if (AcceptClientConnection(events[i].fd, &s) == STATUS_OK)
+ BackendStartup(&s);
/*
* We no longer need the open socket or port structure in this
* process
*/
- if (port.sock != PGINVALID_SOCKET)
- StreamClose(port.sock);
+ if (s.sock != PGINVALID_SOCKET)
+ {
+ if (closesocket(s.sock) != 0)
+ elog(LOG, "could not close client socket: %m");
+ }
}
}
@@ -2529,7 +2531,8 @@ ClosePostmasterPorts(bool am_syslogger)
{
if (ListenSocket[i] != PGINVALID_SOCKET)
{
- StreamClose(ListenSocket[i]);
+ if (closesocket(ListenSocket[i]) != 0)
+ elog(LOG, "could not close listen socket: %m");
ListenSocket[i] = PGINVALID_SOCKET;
}
}
@@ -4034,7 +4037,7 @@ TerminateChildren(int signal)
* Note: if you change this code, also consider StartAutovacuumWorker.
*/
static int
-BackendStartup(Port *port)
+BackendStartup(ClientSocket *client_sock)
{
Backend *bn; /* for backend cleanup */
pid_t pid;
@@ -4085,7 +4088,7 @@ BackendStartup(Port *port)
bn->bgworker_notify = false;
#ifdef EXEC_BACKEND
- pid = backend_forkexec(port, cac);
+ pid = backend_forkexec(client_sock, cac);
#else /* !EXEC_BACKEND */
pid = fork_process();
if (pid == 0) /* child */
@@ -4097,10 +4100,10 @@ BackendStartup(Port *port)
ClosePostmasterPorts(false);
/* Perform additional initialization and collect startup packet */
- BackendInitialize(port, cac);
+ BackendInitialize(client_sock, cac);
/* And run the backend */
- BackendRun(port);
+ BackendRun();
}
#endif /* EXEC_BACKEND */
@@ -4115,14 +4118,14 @@ BackendStartup(Port *port)
errno = save_errno;
ereport(LOG,
(errmsg("could not fork new process for connection: %m")));
- report_fork_failure_to_client(port, save_errno);
+ report_fork_failure_to_client(client_sock, 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)));
+ (int) pid, (int) client_sock->sock)));
/*
* Everything's been successful, it's safe to add this backend to our list
@@ -4149,7 +4152,7 @@ BackendStartup(Port *port)
* it's not up and running.
*/
static void
-report_fork_failure_to_client(Port *port, int errnum)
+report_fork_failure_to_client(ClientSocket *client_sock, int errnum)
{
char buffer[1000];
int rc;
@@ -4160,13 +4163,13 @@ report_fork_failure_to_client(Port *port, int errnum)
strerror(errnum));
/* Set port to non-blocking. Don't do send() if this fails */
- if (!pg_set_noblock(port->sock))
+ if (!pg_set_noblock(client_sock->sock))
return;
/* We'll retry after EINTR, but ignore all other failures */
do
{
- rc = send(port->sock, buffer, strlen(buffer) + 1, 0);
+ rc = send(client_sock->sock, buffer, strlen(buffer) + 1, 0);
} while (rc < 0 && errno == EINTR);
}
@@ -4184,16 +4187,24 @@ report_fork_failure_to_client(Port *port, int errnum)
* but have not yet set up most of our local pointers to shmem structures.
*/
static void
-BackendInitialize(Port *port, CAC_state cac)
+BackendInitialize(ClientSocket *client_sock, CAC_state cac)
{
int status;
int ret;
+ Port *port;
char remote_host[NI_MAXHOST];
char remote_port[NI_MAXSERV];
StringInfoData ps_data;
+ MemoryContext oldcontext;
- /* Save port etc. for ps status */
+ /*
+ * Create Port structure in TopMemoryContext, so that it survives into
+ * PostgresMain execution.
+ */
+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+ port = StreamConnection(client_sock);
MyProcPort = port;
+ MemoryContextSwitchTo(oldcontext);
/* Tell fd.c about the long-lived FD associated with the port */
ReserveExternalFD();
@@ -4254,8 +4265,9 @@ BackendInitialize(Port *port, CAC_state cac)
* Save remote_host and remote_port in port structure (after this, they
* will appear in log_line_prefix data for log messages).
*/
- port->remote_host = strdup(remote_host);
- port->remote_port = strdup(remote_port);
+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+ port->remote_host = pstrdup(remote_host);
+ port->remote_port = pstrdup(remote_port);
/* And now we can issue the Log_connections message, if wanted */
if (Log_connections)
@@ -4286,7 +4298,8 @@ BackendInitialize(Port *port, CAC_state cac)
ret == 0 &&
strspn(remote_host, "0123456789.") < strlen(remote_host) &&
strspn(remote_host, "0123456789ABCDEFabcdef:") < strlen(remote_host))
- port->remote_hostname = strdup(remote_host);
+ port->remote_hostname = pstrdup(remote_host);
+ MemoryContextSwitchTo(oldcontext);
/*
* Ready to begin client interaction. We will give up and _exit(1) after
@@ -4407,11 +4420,11 @@ BackendInitialize(Port *port, CAC_state cac)
* Doesn't return at all.
*/
static void
-BackendRun(Port *port)
+BackendRun(void)
{
/*
- * Create a per-backend PGPROC struct in shared memory. We must do
- * this before we can use LWLocks (in AttachSharedMemoryAndSemaphores).
+ * Create a per-backend PGPROC struct in shared memory. We must do this
+ * before we can use LWLocks (in AttachSharedMemoryAndSemaphores).
*/
InitProcess();
@@ -4424,7 +4437,7 @@ BackendRun(Port *port)
*/
MemoryContextSwitchTo(TopMemoryContext);
- PostgresMain(port->database_name, port->user_name);
+ PostgresMain(MyProcPort->database_name, MyProcPort->user_name);
}
@@ -4445,11 +4458,11 @@ BackendRun(Port *port)
pid_t
postmaster_forkexec(int argc, char *argv[])
{
- Port port;
+ ClientSocket client_sock;
- /* This entry point passes dummy values for the Port variables */
- memset(&port, 0, sizeof(port));
- return internal_forkexec(argc, argv, &port);
+ /* This entry point doesn't pass a client socket */
+ memset(&client_sock, 0, sizeof(ClientSocket));
+ return internal_forkexec(argc, argv, &client_sock);
}
/*
@@ -4462,7 +4475,7 @@ postmaster_forkexec(int argc, char *argv[])
* returns the pid of the fork/exec'd process, or -1 on failure
*/
static pid_t
-backend_forkexec(Port *port, CAC_state cac)
+backend_forkexec(ClientSocket *client_sock, CAC_state cac)
{
char *av[5];
int ac = 0;
@@ -4478,7 +4491,7 @@ backend_forkexec(Port *port, CAC_state cac)
av[ac] = NULL;
Assert(ac < lengthof(av));
- return internal_forkexec(ac, av, port);
+ return internal_forkexec(ac, av, client_sock);
}
#ifndef WIN32
@@ -4490,7 +4503,7 @@ backend_forkexec(Port *port, CAC_state cac)
* - 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[], ClientSocket *client_sock)
{
static unsigned long tmpBackendFileNum = 0;
pid_t pid;
@@ -4498,7 +4511,7 @@ internal_forkexec(int argc, char *argv[], Port *port)
BackendParameters param;
FILE *fp;
- if (!save_backend_variables(¶m, port))
+ if (!save_backend_variables(¶m, client_sock))
return -1; /* log made by save_backend_variables */
/* Calculate name for temp file */
@@ -4796,7 +4809,7 @@ retry:
void
SubPostmasterMain(int argc, char *argv[])
{
- Port port;
+ ClientSocket client_sock;
/* In EXEC_BACKEND case we will not have inherited these settings */
IsPostmasterEnvironment = true;
@@ -4810,8 +4823,8 @@ SubPostmasterMain(int argc, char *argv[])
elog(FATAL, "invalid subpostmaster invocation");
/* Read in the variables file */
- memset(&port, 0, sizeof(Port));
- read_backend_variables(argv[2], &port);
+ memset(&client_sock, 0, sizeof(ClientSocket));
+ read_backend_variables(argv[2], &client_sock);
/* Close the postmaster's sockets (as soon as we know them) */
ClosePostmasterPorts(strcmp(argv[1], "--forklog") == 0);
@@ -4915,13 +4928,13 @@ SubPostmasterMain(int argc, char *argv[])
* PGPROC slots, we have already initialized libpq and are able to
* report the error to the client.
*/
- BackendInitialize(&port, cac);
+ BackendInitialize(&client_sock, cac);
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
/* And run the backend */
- BackendRun(&port); /* does not return */
+ BackendRun(); /* does not return */
}
if (strcmp(argv[1], "--forkaux") == 0)
{
@@ -5995,15 +6008,15 @@ static void read_inheritable_socket(SOCKET *dest, InheritableSocket *src);
/* Save critical backend variables into the BackendParameters struct */
#ifndef WIN32
static bool
-save_backend_variables(BackendParameters *param, Port *port)
+save_backend_variables(BackendParameters *param, ClientSocket *client_sock)
#else
static bool
-save_backend_variables(BackendParameters *param, Port *port,
+save_backend_variables(BackendParameters *param, ClientSocket *client_sock,
HANDLE childProcess, pid_t childPid)
#endif
{
- memcpy(¶m->port, port, sizeof(Port));
- if (!write_inheritable_socket(¶m->portsocket, port->sock, childPid))
+ memcpy(¶m->client_sock, client_sock, sizeof(ClientSocket));
+ if (!write_inheritable_socket(¶m->serialized_sock, client_sock->sock, childPid))
return false;
strlcpy(param->DataDir, DataDir, MAXPGPATH);
@@ -6165,7 +6178,7 @@ read_inheritable_socket(SOCKET *dest, InheritableSocket *src)
#endif
static void
-read_backend_variables(char *id, Port *port)
+read_backend_variables(char *id, ClientSocket *client_sock)
{
BackendParameters param;
@@ -6232,15 +6245,15 @@ read_backend_variables(char *id, Port *port)
}
#endif
- restore_backend_variables(¶m, port);
+ restore_backend_variables(¶m, client_sock);
}
/* Restore critical backend variables from the BackendParameters struct */
static void
-restore_backend_variables(BackendParameters *param, Port *port)
+restore_backend_variables(BackendParameters *param, ClientSocket *client_sock)
{
- memcpy(port, ¶m->port, sizeof(Port));
- read_inheritable_socket(&port->sock, ¶m->portsocket);
+ memcpy(client_sock, ¶m->client_sock, sizeof(ClientSocket));
+ read_inheritable_socket(&client_sock->sock, ¶m->serialized_sock);
SetDataDir(param->DataDir);
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 625caa853a0..41867dc14ac 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -110,12 +110,9 @@ typedef struct ClientConnectionInfo
} ClientConnectionInfo;
/*
- * This is used by the postmaster in its communication with frontends. It
- * contains all state information needed during this communication before the
- * backend is run. The Port structure is kept in malloc'd memory and is
- * still available when a backend is running (see MyProcPort). The data
- * it points to must also be malloc'd, or else palloc'd in TopMemoryContext,
- * so that it survives into PostgresMain execution!
+ * The Port structure holds state information about a client connection in a
+ * backend process. It is available in the global variable MyProcPort. The
+ * struct and all the data it points are kept in TopMemoryContext.
*
* remote_hostname is set if we did a successful reverse lookup of the
* client's IP address during connection setup.
@@ -217,6 +214,16 @@ typedef struct Port
#endif
} Port;
+/*
+ * ClientSocket holds a socket for an accepted connection, along with the
+ * information about the endpoints.
+ */
+typedef struct ClientSocket {
+ pgsocket sock; /* File descriptor */
+ SockAddr laddr; /* local addr (postmaster) */
+ SockAddr raddr; /* remote addr (client) */
+} ClientSocket;
+
#ifdef USE_SSL
/*
* Hardcoded DH parameters, used in ephemeral DH keying. (See also
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 50fc781f471..fcbcdbe2dbf 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -64,11 +64,11 @@ extern PGDLLIMPORT WaitEventSet *FeBeWaitSet;
#define FeBeWaitSetLatchPos 1
#define FeBeWaitSetNEvents 3
-extern int StreamServerPort(int family, const char *hostName,
+extern int ListenServerPort(int family, const char *hostName,
unsigned short portNumber, const char *unixSocketDir,
pgsocket ListenSocket[], int MaxListen);
-extern int StreamConnection(pgsocket server_fd, Port *port);
-extern void StreamClose(pgsocket sock);
+extern int AcceptClientConnection(pgsocket server_fd, ClientSocket *client_sock);
+extern Port *StreamConnection(ClientSocket *client_sock);
extern void TouchSocketFiles(void);
extern void RemoveSocketFiles(void);
extern void pq_init(void);
--
2.30.2
[text/x-patch] 0009-Refactor-postmaster-child-process-launching.patch (136.9K, ../[email protected]/10-0009-Refactor-postmaster-child-process-launching.patch)
download | inline diff:
From b33cfeb28a5419045acb659a01410b2b463bea3e Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Sun, 18 Jun 2023 13:59:48 +0300
Subject: [PATCH 9/9] Refactor postmaster child process launching
- Move code related to launching backend processes to new source file,
process_start.c
- Introduce new postmaster_child_launch() function that deals with the
differences between EXEC_BACKEND and fork mode.
- Refactor the mechanism of passing informaton from the parent to
child process. Instead of using different command-line arguments
when launching the child process in EXEC_BACKEND mode, pass a
variable-length blob of data along with all the global
variables. The contents of that blob depends on the kind of child
process being launched. In !EXEC_BACKEND mode, we use the same blob,
but it's simply inherited from the parent to child process.
---
src/backend/postmaster/Makefile | 2 +-
src/backend/postmaster/autovacuum.c | 173 +--
src/backend/postmaster/auxprocess.c | 67 +-
src/backend/postmaster/bgworker.c | 19 +-
src/backend/postmaster/bgwriter.c | 9 +-
src/backend/postmaster/checkpointer.c | 9 +-
src/backend/postmaster/fork_process.c | 126 --
src/backend/postmaster/launch_backend.c | 1314 ++++++++++++++++
src/backend/postmaster/meson.build | 2 +-
src/backend/postmaster/pgarch.c | 9 +-
src/backend/postmaster/postmaster.c | 1526 +++----------------
src/backend/postmaster/startup.c | 9 +-
src/backend/postmaster/syslogger.c | 275 ++--
src/backend/postmaster/walwriter.c | 9 +-
src/backend/replication/logical/launcher.c | 1 -
src/backend/replication/walreceiver.c | 10 +-
src/backend/storage/ipc/shmem.c | 2 +
src/backend/tcop/postgres.c | 1 +
src/backend/utils/init/globals.c | 1 +
src/backend/utils/init/miscinit.c | 133 --
src/include/libpq/libpq-be.h | 10 +-
src/include/miscadmin.h | 1 -
src/include/postmaster/autovacuum.h | 12 +-
src/include/postmaster/auxprocess.h | 4 +-
src/include/postmaster/bgworker_internals.h | 4 +-
src/include/postmaster/bgwriter.h | 4 +-
src/include/postmaster/fork_process.h | 17 -
src/include/postmaster/pgarch.h | 2 +-
src/include/postmaster/postmaster.h | 40 +-
src/include/postmaster/startup.h | 2 +-
src/include/postmaster/syslogger.h | 4 +-
src/include/postmaster/walwriter.h | 2 +-
src/include/replication/walreceiver.h | 2 +-
33 files changed, 1799 insertions(+), 2002 deletions(-)
delete mode 100644 src/backend/postmaster/fork_process.c
create mode 100644 src/backend/postmaster/launch_backend.c
delete mode 100644 src/include/postmaster/fork_process.h
diff --git a/src/backend/postmaster/Makefile b/src/backend/postmaster/Makefile
index 047448b34eb..fc88f5bae0b 100644
--- a/src/backend/postmaster/Makefile
+++ b/src/backend/postmaster/Makefile
@@ -18,8 +18,8 @@ OBJS = \
bgworker.o \
bgwriter.o \
checkpointer.o \
- fork_process.o \
interrupt.o \
+ launch_backend.o \
pgarch.o \
postmaster.o \
startup.o \
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1041376951e..e13284b4b5c 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -84,7 +84,6 @@
#include "nodes/makefuncs.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
-#include "postmaster/fork_process.h"
#include "postmaster/interrupt.h"
#include "postmaster/postmaster.h"
#include "storage/bufmgr.h"
@@ -315,13 +314,6 @@ static WorkerInfo MyWorkerInfo = NULL;
/* PID of launcher, valid only in worker while shutting down */
int AutovacuumLauncherPid = 0;
-#ifdef EXEC_BACKEND
-static pid_t avlauncher_forkexec(void);
-static pid_t avworker_forkexec(void);
-#endif
-NON_EXEC_STATIC void AutoVacWorkerMain(int argc, char *argv[]) pg_attribute_noreturn();
-NON_EXEC_STATIC void AutoVacLauncherMain(int argc, char *argv[]) pg_attribute_noreturn();
-
static Oid do_start_worker(void);
static void HandleAutoVacLauncherInterrupts(void);
static void AutoVacLauncherShutdown(void) pg_attribute_noreturn();
@@ -365,85 +357,21 @@ static void avl_sigusr2_handler(SIGNAL_ARGS);
* AUTOVACUUM LAUNCHER CODE
********************************************************************/
-#ifdef EXEC_BACKEND
/*
- * forkexec routine for the autovacuum launcher process.
- *
- * Format up the arglist, then fork and exec.
- */
-static pid_t
-avlauncher_forkexec(void)
-{
- char *av[10];
- int ac = 0;
-
- av[ac++] = "postgres";
- av[ac++] = "--forkavlauncher";
- av[ac++] = NULL; /* filled in by postmaster_forkexec */
- av[ac] = NULL;
-
- Assert(ac < lengthof(av));
-
- return postmaster_forkexec(ac, av);
-}
-
-/*
- * We need this set from the outside, before InitProcess is called
+ * Main loop for the autovacuum launcher process.
*/
void
-AutovacuumLauncherIAm(void)
-{
- am_autovacuum_launcher = true;
-}
-#endif
-
-/*
- * Main entry point for autovacuum launcher process, to be called from the
- * postmaster.
- */
-int
-StartAutoVacLauncher(void)
+AutoVacLauncherMain(char *startup_data, size_t startup_data_len)
{
- pid_t AutoVacPID;
+ sigjmp_buf local_sigjmp_buf;
-#ifdef EXEC_BACKEND
- switch ((AutoVacPID = avlauncher_forkexec()))
-#else
- switch ((AutoVacPID = fork_process()))
-#endif
+ /* Release postmaster's working memory context */
+ if (PostmasterContext)
{
- case -1:
- ereport(LOG,
- (errmsg("could not fork autovacuum launcher process: %m")));
- return 0;
-
-#ifndef EXEC_BACKEND
- case 0:
- /* in postmaster child ... */
- InitPostmasterChild();
-
- /* Close the postmaster's sockets */
- ClosePostmasterPorts(false);
-
- AutoVacLauncherMain(0, NULL);
- break;
-#endif
- default:
- return (int) AutoVacPID;
+ MemoryContextDelete(PostmasterContext);
+ PostmasterContext = NULL;
}
- /* shouldn't get here */
- return 0;
-}
-
-/*
- * Main loop for the autovacuum launcher process.
- */
-NON_EXEC_STATIC void
-AutoVacLauncherMain(int argc, char *argv[])
-{
- sigjmp_buf local_sigjmp_buf;
-
am_autovacuum_launcher = true;
MyBackendType = B_AUTOVAC_LAUNCHER;
@@ -475,6 +403,9 @@ AutoVacLauncherMain(int argc, char *argv[])
pqsignal(SIGFPE, FloatExceptionHandler);
pqsignal(SIGCHLD, SIG_DFL);
+ /* autovacuum needs this set before calling InitProcess */
+ am_autovacuum_launcher = true;
+
/*
* Create a per-backend PGPROC struct in shared memory. We must do this
* before we can use LWLocks.
@@ -1435,87 +1366,22 @@ avl_sigusr2_handler(SIGNAL_ARGS)
* AUTOVACUUM WORKER CODE
********************************************************************/
-#ifdef EXEC_BACKEND
-/*
- * forkexec routines for the autovacuum worker.
- *
- * Format up the arglist, then fork and exec.
- */
-static pid_t
-avworker_forkexec(void)
-{
- char *av[10];
- int ac = 0;
-
- av[ac++] = "postgres";
- av[ac++] = "--forkavworker";
- av[ac++] = NULL; /* filled in by postmaster_forkexec */
- av[ac] = NULL;
-
- Assert(ac < lengthof(av));
-
- return postmaster_forkexec(ac, av);
-}
-
/*
- * We need this set from the outside, before InitProcess is called
+ * AutoVacWorkerMain
*/
void
-AutovacuumWorkerIAm(void)
+AutoVacWorkerMain(char *startup_data, size_t startup_data_len)
{
- am_autovacuum_worker = true;
-}
-#endif
-
-/*
- * Main entry point for autovacuum worker process.
- *
- * This code is heavily based on pgarch.c, q.v.
- */
-int
-StartAutoVacWorker(void)
-{
- pid_t worker_pid;
+ sigjmp_buf local_sigjmp_buf;
+ Oid dbid;
-#ifdef EXEC_BACKEND
- switch ((worker_pid = avworker_forkexec()))
-#else
- switch ((worker_pid = fork_process()))
-#endif
+ /* Release postmaster's working memory context */
+ if (PostmasterContext)
{
- case -1:
- ereport(LOG,
- (errmsg("could not fork autovacuum worker process: %m")));
- return 0;
-
-#ifndef EXEC_BACKEND
- case 0:
- /* in postmaster child ... */
- InitPostmasterChild();
-
- /* Close the postmaster's sockets */
- ClosePostmasterPorts(false);
-
- AutoVacWorkerMain(0, NULL);
- break;
-#endif
- default:
- return (int) worker_pid;
+ MemoryContextDelete(PostmasterContext);
+ PostmasterContext = NULL;
}
- /* shouldn't get here */
- return 0;
-}
-
-/*
- * AutoVacWorkerMain
- */
-NON_EXEC_STATIC void
-AutoVacWorkerMain(int argc, char *argv[])
-{
- sigjmp_buf local_sigjmp_buf;
- Oid dbid;
-
am_autovacuum_worker = true;
MyBackendType = B_AUTOVAC_WORKER;
@@ -1546,6 +1412,9 @@ AutoVacWorkerMain(int argc, char *argv[])
pqsignal(SIGFPE, FloatExceptionHandler);
pqsignal(SIGCHLD, SIG_DFL);
+ /* autovacuum needs this set before calling InitProcess */
+ am_autovacuum_worker = true;
+
/*
* Create a per-backend PGPROC struct in shared memory. We must do this
* before we can use LWLocks.
diff --git a/src/backend/postmaster/auxprocess.c b/src/backend/postmaster/auxprocess.c
index 536d9a2b3e4..76de39df003 100644
--- a/src/backend/postmaster/auxprocess.c
+++ b/src/backend/postmaster/auxprocess.c
@@ -46,7 +46,7 @@ AuxProcType MyAuxProcType = NotAnAuxProcess; /* declared in miscadmin.h */
/*
- * AuxiliaryProcessMain
+ * AuxiliaryProcessMain XXX
*
* The main entry point for auxiliary processes, such as the bgwriter,
* walwriter, walreceiver, bootstrapper and the shared memory checker code.
@@ -54,37 +54,17 @@ AuxProcType MyAuxProcType = NotAnAuxProcess; /* declared in miscadmin.h */
* This code is here just because of historical reasons.
*/
void
-AuxiliaryProcessMain(AuxProcType auxtype)
+AuxiliaryProcessInit(void)
{
- Assert(IsUnderPostmaster);
-
- MyAuxProcType = auxtype;
-
- switch (MyAuxProcType)
+ /* Release postmaster's working memory context */
+ if (PostmasterContext)
{
- case StartupProcess:
- MyBackendType = B_STARTUP;
- break;
- case ArchiverProcess:
- MyBackendType = B_ARCHIVER;
- break;
- case BgWriterProcess:
- MyBackendType = B_BG_WRITER;
- break;
- case CheckpointerProcess:
- MyBackendType = B_CHECKPOINTER;
- break;
- case WalWriterProcess:
- MyBackendType = B_WAL_WRITER;
- break;
- case WalReceiverProcess:
- MyBackendType = B_WAL_RECEIVER;
- break;
- default:
- elog(PANIC, "unrecognized process type: %d", (int) MyAuxProcType);
- MyBackendType = B_INVALID;
+ MemoryContextDelete(PostmasterContext);
+ PostmasterContext = NULL;
}
+ Assert(IsUnderPostmaster);
+
init_ps_display(NULL);
SetProcessingMode(BootstrapProcessing);
@@ -134,37 +114,6 @@ AuxiliaryProcessMain(AuxProcType auxtype)
before_shmem_exit(ShutdownAuxiliaryProcess, 0);
SetProcessingMode(NormalProcessing);
-
- switch (MyAuxProcType)
- {
- case StartupProcess:
- StartupProcessMain();
- proc_exit(1);
-
- case ArchiverProcess:
- PgArchiverMain();
- proc_exit(1);
-
- case BgWriterProcess:
- BackgroundWriterMain();
- proc_exit(1);
-
- case CheckpointerProcess:
- CheckpointerMain();
- proc_exit(1);
-
- case WalWriterProcess:
- WalWriterMain();
- proc_exit(1);
-
- case WalReceiverProcess:
- WalReceiverMain();
- proc_exit(1);
-
- default:
- elog(PANIC, "unrecognized process type: %d", (int) MyAuxProcType);
- proc_exit(1);
- }
}
/*
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 4c446ffd087..97d62777c8a 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -717,23 +717,30 @@ bgworker_die(SIGNAL_ARGS)
}
/*
- * Start a new background worker
- *
- * This is the main entry point for background worker, to be called from
- * postmaster.
+ * This is the main entry point for background worker process.
*/
void
-StartBackgroundWorker(void)
+BackgroundWorkerMain(char *startup_data, size_t startup_data_len)
{
sigjmp_buf local_sigjmp_buf;
- BackgroundWorker *worker = MyBgworkerEntry;
+ BackgroundWorker *worker;
bgworker_main_type entrypt;
+ /* Release postmaster's working memory context */
+ if (PostmasterContext)
+ {
+ MemoryContextDelete(PostmasterContext);
+ PostmasterContext = NULL;
+ }
+
+ Assert(startup_data_len == sizeof(BackgroundWorker));
+ worker = (BackgroundWorker *) startup_data;
if (worker == NULL)
elog(FATAL, "unable to find bgworker entry");
IsBackgroundWorker = true;
+ MyBgworkerEntry = worker;
MyBackendType = B_BG_WORKER;
init_ps_display(worker->bgw_name);
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index caad642ec93..0b6fbec1dbe 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -36,6 +36,7 @@
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/auxprocess.h"
#include "postmaster/bgwriter.h"
#include "postmaster/interrupt.h"
#include "storage/buf_internals.h"
@@ -88,13 +89,19 @@ static XLogRecPtr last_snapshot_lsn = InvalidXLogRecPtr;
* basic execution environment, but not enabled signals yet.
*/
void
-BackgroundWriterMain(void)
+BackgroundWriterMain(char *startup_data, size_t startup_data_len)
{
sigjmp_buf local_sigjmp_buf;
MemoryContext bgwriter_context;
bool prev_hibernate;
WritebackContext wb_context;
+ Assert(startup_data_len == 0);
+
+ MyAuxProcType = BgWriterProcess;
+ MyBackendType = B_BG_WRITER;
+ AuxiliaryProcessInit();
+
/*
* Properly accept or ignore signals that might be sent to us.
*/
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index ace9893d957..e617066c385 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -42,6 +42,7 @@
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/auxprocess.h"
#include "postmaster/bgwriter.h"
#include "postmaster/interrupt.h"
#include "replication/syncrep.h"
@@ -178,11 +179,17 @@ static void ReqCheckpointHandler(SIGNAL_ARGS);
* basic execution environment, but not enabled signals yet.
*/
void
-CheckpointerMain(void)
+CheckpointerMain(char *startup_data, size_t startup_data_len)
{
sigjmp_buf local_sigjmp_buf;
MemoryContext checkpointer_context;
+ Assert(startup_data_len == 0);
+
+ MyAuxProcType = CheckpointerProcess;
+ MyBackendType = B_CHECKPOINTER;
+ AuxiliaryProcessInit();
+
CheckpointerShmem->checkpointer_pid = MyProcPid;
/*
diff --git a/src/backend/postmaster/fork_process.c b/src/backend/postmaster/fork_process.c
deleted file mode 100644
index 6f9c2765d68..00000000000
--- a/src/backend/postmaster/fork_process.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * fork_process.c
- * A simple wrapper on top of fork(). This does not handle the
- * EXEC_BACKEND case; it might be extended to do so, but it would be
- * considerably more complex.
- *
- * Copyright (c) 1996-2023, PostgreSQL Global Development Group
- *
- * IDENTIFICATION
- * src/backend/postmaster/fork_process.c
- */
-#include "postgres.h"
-
-#include <fcntl.h>
-#include <signal.h>
-#include <time.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <unistd.h>
-
-#include "libpq/pqsignal.h"
-#include "postmaster/fork_process.h"
-
-#ifndef WIN32
-/*
- * Wrapper for fork(). Return values are the same as those for fork():
- * -1 if the fork failed, 0 in the child process, and the PID of the
- * child in the parent process. Signals are blocked while forking, so
- * the child must unblock.
- */
-pid_t
-fork_process(void)
-{
- pid_t result;
- const char *oomfilename;
- sigset_t save_mask;
-
-#ifdef LINUX_PROFILE
- struct itimerval prof_itimer;
-#endif
-
- /*
- * Flush stdio channels just before fork, to avoid double-output problems.
- */
- fflush(NULL);
-
-#ifdef LINUX_PROFILE
-
- /*
- * Linux's fork() resets the profiling timer in the child process. If we
- * want to profile child processes then we need to save and restore the
- * timer setting. This is a waste of time if not profiling, however, so
- * only do it if commanded by specific -DLINUX_PROFILE switch.
- */
- getitimer(ITIMER_PROF, &prof_itimer);
-#endif
-
- /*
- * We start postmaster children with signals blocked. This allows them to
- * install their own handlers before unblocking, to avoid races where they
- * might run the postmaster's handler and miss an important control
- * signal. With more analysis this could potentially be relaxed.
- */
- sigprocmask(SIG_SETMASK, &BlockSig, &save_mask);
- result = fork();
- if (result == 0)
- {
- /* fork succeeded, in child */
-#ifdef LINUX_PROFILE
- setitimer(ITIMER_PROF, &prof_itimer, NULL);
-#endif
-
- /*
- * By default, Linux tends to kill the postmaster in out-of-memory
- * situations, because it blames the postmaster for the sum of child
- * process sizes *including shared memory*. (This is unbelievably
- * stupid, but the kernel hackers seem uninterested in improving it.)
- * Therefore it's often a good idea to protect the postmaster by
- * setting its OOM score adjustment negative (which has to be done in
- * a root-owned startup script). Since the adjustment is inherited by
- * child processes, this would ordinarily mean that all the
- * postmaster's children are equally protected against OOM kill, which
- * is not such a good idea. So we provide this code to allow the
- * children to change their OOM score adjustments again. Both the
- * file name to write to and the value to write are controlled by
- * environment variables, which can be set by the same startup script
- * that did the original adjustment.
- */
- oomfilename = getenv("PG_OOM_ADJUST_FILE");
-
- if (oomfilename != NULL)
- {
- /*
- * Use open() not stdio, to ensure we control the open flags. Some
- * Linux security environments reject anything but O_WRONLY.
- */
- int fd = open(oomfilename, O_WRONLY, 0);
-
- /* We ignore all errors */
- if (fd >= 0)
- {
- const char *oomvalue = getenv("PG_OOM_ADJUST_VALUE");
- int rc;
-
- if (oomvalue == NULL) /* supply a useful default */
- oomvalue = "0";
-
- rc = write(fd, oomvalue, strlen(oomvalue));
- (void) rc;
- close(fd);
- }
- }
-
- /* do post-fork initialization for random number generation */
- pg_strong_random_init();
- }
- else
- {
- /* in parent, restore signal mask */
- sigprocmask(SIG_SETMASK, &save_mask, NULL);
- }
-
- return result;
-}
-
-#endif /* ! WIN32 */
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
new file mode 100644
index 00000000000..b1fc783a576
--- /dev/null
+++ b/src/backend/postmaster/launch_backend.c
@@ -0,0 +1,1314 @@
+/*-------------------------------------------------------------------------
+ *
+ * launch_backend.c
+ * Functions for launching backends and other postmaster child
+ * processes.
+ *
+ * - explain EXEC_BACKEND and Windows
+ * - postmaster calls postmaster_child_launch()
+ * - the child process will be restored to roughly the same state, whether
+ * EXEC_BACKEND is used or not: it will be attached to shared memory,
+ * and fds and other resources that we've inherited from postmaster that are not needed
+ * in a child process have been closed.
+ *
+ * When a request message is received, we now fork() immediately.
+ * The child process performs authentication of the request, and
+ * then becomes a backend if successful. This allows the auth code
+ * to be written in a simple single-threaded style (as opposed to the
+ * crufty "poor man's multitasking" code that used to be needed).
+ * More importantly, it ensures that blockages in non-multithreaded
+ * libraries like SSL or PAM cannot cause denial of service to other
+ * clients.
+ *
+ *
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/postmaster/launch_backend.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <netdb.h>
+#include <limits.h>
+
+#include "access/transam.h"
+#include "access/xlog.h"
+#include "libpq/libpq-be.h"
+#include "libpq/pqsignal.h"
+#include "miscadmin.h"
+#include "nodes/queryjumble.h"
+#include "port.h"
+#include "postmaster/autovacuum.h"
+#include "postmaster/auxprocess.h"
+#include "postmaster/bgworker_internals.h"
+#include "postmaster/bgwriter.h"
+#include "postmaster/interrupt.h"
+#include "postmaster/pgarch.h"
+#include "postmaster/postmaster.h"
+#include "postmaster/startup.h"
+#include "postmaster/syslogger.h"
+#include "postmaster/walwriter.h"
+#include "replication/walreceiver.h"
+#include "storage/fd.h"
+#include "storage/ipc.h"
+#include "storage/pg_shmem.h"
+#include "storage/pmsignal.h"
+#include "storage/proc.h"
+#include "tcop/tcopprot.h"
+#include "utils/builtins.h"
+#include "utils/datetime.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+#include "utils/ps_status.h"
+#include "utils/timeout.h"
+#include "utils/timestamp.h"
+
+#ifdef EXEC_BACKEND
+#include "storage/spin.h"
+#endif
+
+
+/* Type for a socket that can be inherited to a client process */
+#ifdef WIN32
+typedef struct
+{
+ SOCKET origsocket; /* Original socket value, or PGINVALID_SOCKET
+ * if not a socket */
+ WSAPROTOCOL_INFO wsainfo;
+} InheritableSocket;
+#else
+typedef int InheritableSocket;
+#endif
+
+#ifdef EXEC_BACKEND
+
+/*
+ * Structure contains all global variables passed to exec:ed backends
+ */
+typedef struct
+{
+ char DataDir[MAXPGPATH];
+ int32 MyCancelKey;
+ int MyPMChildSlot;
+#ifndef WIN32
+ unsigned long UsedShmemSegID;
+#else
+ void *ShmemProtectiveRegion;
+ HANDLE UsedShmemSegID;
+#endif
+ void *UsedShmemSegAddr;
+ slock_t *ShmemLock;
+ VariableCache ShmemVariableCache;
+ struct bkend *ShmemBackendArray;
+#ifndef HAVE_SPINLOCKS
+ PGSemaphore *SpinlockSemaArray;
+#endif
+ int NamedLWLockTrancheRequests;
+ NamedLWLockTranche *NamedLWLockTrancheArray;
+ LWLockPadded *MainLWLockArray;
+ slock_t *ProcStructLock;
+ PROC_HDR *ProcGlobal;
+ PGPROC *AuxiliaryProcs;
+ PGPROC *PreparedXactProcs;
+ PMSignalData *PMSignalState;
+ pid_t PostmasterPid;
+ TimestampTz PgStartTime;
+ TimestampTz PgReloadTime;
+ pg_time_t first_syslogger_file_time;
+ bool redirection_done;
+ bool IsBinaryUpgrade;
+ bool query_id_enabled;
+ int max_safe_fds;
+ int MaxBackends;
+#ifdef WIN32
+ HANDLE PostmasterHandle;
+ HANDLE initial_signal_pipe;
+ HANDLE syslogPipe[2];
+#else
+ int postmaster_alive_fds[2];
+ int syslogPipe[2];
+#endif
+ char my_exec_path[MAXPGPATH];
+ char pkglib_path[MAXPGPATH];
+
+ /*
+ * These are only used by backend processes, but it's here because passing
+ * a socket needs some special handling on Windows. 'client_sock' is an
+ * explicit argument to postmaster_child_launch, but is stored in
+ * MyClientSocket in the child process.
+ */
+ ClientSocket client_sock;
+ InheritableSocket inh_sock;
+} BackendParameters;
+
+static void read_backend_variables(char *id, char **startup_data, size_t *startup_data_len);
+static void restore_backend_variables(BackendParameters *param);
+
+#ifndef WIN32
+static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock);
+#else
+static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock,
+ HANDLE childProcess, pid_t childPid);
+#endif
+#endif /* EXEC_BACKEND */
+
+static void InitPostmasterChild(bool am_syslogger);
+
+
+#ifdef EXEC_BACKEND
+static pid_t internal_forkexec(const char *entry_name, char *startup_data, size_t startup_data_len, ClientSocket *client_sock);
+#endif /* EXEC_BACKEND */
+
+#ifndef WIN32
+static pid_t fork_process(void);
+#endif
+
+typedef struct PMChildEntry
+{
+ const char *name;
+ ChildEntryPoint entry_fn;
+ bool shmem_attach;
+} PMChildEntry;
+
+const PMChildEntry entry_kinds[] = {
+ {"backend", BackendMain, true},
+
+ {"autovacuum launcher", AutoVacLauncherMain, true},
+ {"autovacuum worker", AutoVacWorkerMain, true},
+ {"bgworker", BackgroundWorkerMain, true},
+ {"syslogger", SysLoggerMain, false},
+
+ {"startup", StartupProcessMain, true},
+ {"bgwriter", BackgroundWriterMain, true},
+ {"archiver", PgArchiverMain, true},
+ {"checkpointer", CheckpointerMain, true},
+ {"wal_writer", WalWriterMain, true},
+ {"wal_receiver", WalReceiverMain, true},
+};
+
+const char *
+PostmasterChildName(PostmasterChildType child_type)
+{
+ Assert(child_type >= 0 && child_type < lengthof(entry_kinds));
+ return entry_kinds[child_type].name;
+}
+
+/*
+ * Start a new postmaster child process.
+ *
+ * The startup_data must be a contigous block that is passed to the child
+ * process.
+ *
+ * (In fork mode, it's inherited directly by the child process. In fork+exec mode,
+ * it is written to a file and read back in the child process)
+ */
+pid_t
+postmaster_child_launch(PostmasterChildType child_type, char *startup_data, size_t startup_data_len, ClientSocket *client_sock)
+{
+ pid_t pid;
+
+ Assert(child_type >= 0 && child_type < lengthof(entry_kinds));
+ Assert(IsPostmasterEnvironment && !IsUnderPostmaster);
+
+#ifdef EXEC_BACKEND
+ pid = internal_forkexec(entry_kinds[child_type].name, startup_data, startup_data_len, client_sock);
+ /* the child process will arrive in SubPostmasterMain */
+#else /* !EXEC_BACKEND */
+ pid = fork_process();
+ if (pid == 0) /* child */
+ {
+ /* Detangle from postmaster */
+ InitPostmasterChild(child_type == PMC_SYSLOGGER);
+
+ /*
+ * Before blowing away PostmasterContext, save the startup data
+ */
+ MemoryContextSwitchTo(TopMemoryContext);
+ if (startup_data != NULL)
+ {
+ char *cp = palloc(startup_data_len);
+
+ memcpy(cp, startup_data, startup_data_len);
+ startup_data = cp;
+ }
+
+ if (client_sock)
+ {
+ MyClientSocket = palloc(sizeof(ClientSocket));
+ memcpy(MyClientSocket, client_sock, sizeof(ClientSocket));
+ }
+
+ entry_kinds[child_type].entry_fn(startup_data, startup_data_len);
+ Assert(false);
+ }
+#endif /* EXEC_BACKEND */
+ return pid;
+}
+
+#ifndef WIN32
+/*
+ * Wrapper for fork(). Return values are the same as those for fork():
+ * -1 if the fork failed, 0 in the child process, and the PID of the
+ * child in the parent process. Signals are blocked while forking, so
+ * the child must unblock.
+ *
+ * XXX from fork_process.c file header:
+ * A simple wrapper on top of fork(). This does not handle the
+ * EXEC_BACKEND case; it might be extended to do so, but it would be
+ * considerably more complex.
+ */
+static pid_t
+fork_process(void)
+{
+ pid_t result;
+ const char *oomfilename;
+ sigset_t save_mask;
+
+#ifdef LINUX_PROFILE
+ struct itimerval prof_itimer;
+#endif
+
+ /*
+ * Flush stdio channels just before fork, to avoid double-output problems.
+ */
+ fflush(NULL);
+
+#ifdef LINUX_PROFILE
+
+ /*
+ * Linux's fork() resets the profiling timer in the child process. If we
+ * want to profile child processes then we need to save and restore the
+ * timer setting. This is a waste of time if not profiling, however, so
+ * only do it if commanded by specific -DLINUX_PROFILE switch.
+ */
+ getitimer(ITIMER_PROF, &prof_itimer);
+#endif
+
+ /*
+ * We start postmaster children with signals blocked. This allows them to
+ * install their own handlers before unblocking, to avoid races where they
+ * might run the postmaster's handler and miss an important control
+ * signal. With more analysis this could potentially be relaxed.
+ */
+ sigprocmask(SIG_SETMASK, &BlockSig, &save_mask);
+ result = fork();
+ if (result == 0)
+ {
+ /* fork succeeded, in child */
+#ifdef LINUX_PROFILE
+ setitimer(ITIMER_PROF, &prof_itimer, NULL);
+#endif
+
+ /*
+ * By default, Linux tends to kill the postmaster in out-of-memory
+ * situations, because it blames the postmaster for the sum of child
+ * process sizes *including shared memory*. (This is unbelievably
+ * stupid, but the kernel hackers seem uninterested in improving it.)
+ * Therefore it's often a good idea to protect the postmaster by
+ * setting its OOM score adjustment negative (which has to be done in
+ * a root-owned startup script). Since the adjustment is inherited by
+ * child processes, this would ordinarily mean that all the
+ * postmaster's children are equally protected against OOM kill, which
+ * is not such a good idea. So we provide this code to allow the
+ * children to change their OOM score adjustments again. Both the
+ * file name to write to and the value to write are controlled by
+ * environment variables, which can be set by the same startup script
+ * that did the original adjustment.
+ */
+ oomfilename = getenv("PG_OOM_ADJUST_FILE");
+
+ if (oomfilename != NULL)
+ {
+ /*
+ * Use open() not stdio, to ensure we control the open flags. Some
+ * Linux security environments reject anything but O_WRONLY.
+ */
+ int fd = open(oomfilename, O_WRONLY, 0);
+
+ /* We ignore all errors */
+ if (fd >= 0)
+ {
+ const char *oomvalue = getenv("PG_OOM_ADJUST_VALUE");
+ int rc;
+
+ if (oomvalue == NULL) /* supply a useful default */
+ oomvalue = "0";
+
+ rc = write(fd, oomvalue, strlen(oomvalue));
+ (void) rc;
+ close(fd);
+ }
+ }
+
+ /* do post-fork initialization for random number generation */
+ pg_strong_random_init();
+ }
+ else
+ {
+ /* in parent, restore signal mask */
+ sigprocmask(SIG_SETMASK, &save_mask, NULL);
+ }
+
+ return result;
+}
+
+#endif /* !WIN32 */
+
+#ifdef EXEC_BACKEND
+#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(const char *entry_name, char *startup_data, size_t startup_data_len, ClientSocket *client_sock)
+{
+ static unsigned long tmpBackendFileNum = 0;
+ pid_t pid;
+ char tmpfilename[MAXPGPATH];
+ BackendParameters param;
+ FILE *fp;
+ char *argv[4];
+ char forkav[MAXPGPATH];
+
+ if (!save_backend_variables(¶m, client_sock))
+ return -1; /* log made by save_backend_variables */
+
+ /* Calculate name for temp file */
+ snprintf(tmpfilename, MAXPGPATH, "%s/%s.backend_var.%d.%lu",
+ PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX,
+ MyProcPid, ++tmpBackendFileNum);
+
+ /* Open file */
+ fp = AllocateFile(tmpfilename, PG_BINARY_W);
+ if (!fp)
+ {
+ /*
+ * As in OpenTemporaryFileInTablespace, try to make the temp-file
+ * directory, ignoring errors.
+ */
+ (void) MakePGDirectory(PG_TEMP_FILES_DIR);
+
+ 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(¶m, sizeof(param), 1, fp) != 1)
+ {
+ ereport(LOG,
+ (errcode_for_file_access(),
+ errmsg("could not write to file \"%s\": %m", tmpfilename)));
+ FreeFile(fp);
+ return -1;
+ }
+
+ /* write startup data */
+ if (fwrite((char *) &startup_data_len, sizeof(size_t), 1, fp) != 1)
+ {
+ ereport(LOG,
+ (errcode_for_file_access(),
+ errmsg("could not write to file \"%s\": %m", tmpfilename)));
+ FreeFile(fp);
+ return -1;
+ }
+ if (startup_data_len > 0)
+ {
+ if (fwrite(startup_data, startup_data_len, 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;
+ }
+
+ /* set up argv properly */
+ argv[0] = "postgres";
+ snprintf(forkav, MAXPGPATH, "--forkchild=%s", entry_name);
+ argv[1] = forkav;
+ /* Insert temp file name after --fork argument */
+ argv[2] = tmpfilename;
+ argv[3] = NULL;
+
+ /* Fire off execv in child */
+ if ((pid = fork_process()) == 0)
+ {
+ if (execv(postgres_exec_path, argv) < 0)
+ {
+ ereport(LOG,
+ (errmsg("could not execute server process \"%s\": %m",
+ postgres_exec_path)));
+ /* We're already in the child process here, can't return */
+ exit(1);
+ }
+ }
+
+ 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(const char *entry_name, char *startup_data, size_t startup_data_len, ClientSocket *client_sock)
+{
+ int retry_count = 0;
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ int i;
+ int j;
+ char cmdLine[MAXPGPATH * 2];
+ HANDLE paramHandle;
+ BackendParameters *param;
+ SECURITY_ATTRIBUTES sa;
+ char paramHandleStr[32];
+ win32_deadchild_waitinfo *childinfo;
+ char *argv[4];
+
+ /* Resume here if we need to retry */
+retry:
+
+ /* 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)
+ {
+ ereport(LOG,
+ (errmsg("could not create backend parameter file mapping: error code %lu",
+ GetLastError())));
+ return -1;
+ }
+
+ param = MapViewOfFile(paramHandle, FILE_MAP_WRITE, 0, 0, sizeof(BackendParameters));
+ if (!param)
+ {
+ ereport(LOG,
+ (errmsg("could not map backend parameter memory: error code %lu",
+ GetLastError())));
+ CloseHandle(paramHandle);
+ return -1;
+ }
+
+ /* Insert temp file name after --fork argument */
+#ifdef _WIN64
+ sprintf(paramHandleStr, "%llu", (LONG_PTR) paramHandle);
+#else
+ sprintf(paramHandleStr, "%lu", (DWORD) paramHandle);
+#endif
+
+ /* set up argv properly */
+ argv[0] = "postgres";
+ snprintf(forkav, MAXPGPATH, "--forkchild=%s", entry_name);
+ argv[1] = forkav;
+ /* Insert temp file name after --fork argument */
+ argv[2] = tmpfilename;
+ argv[3] = NULL;
+
+ /* 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')
+ {
+ ereport(LOG,
+ (errmsg("subprocess command line too long")));
+ UnmapViewOfFile(param);
+ CloseHandle(paramHandle);
+ 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))
+ {
+ ereport(LOG,
+ (errmsg("CreateProcess() call failed: %m (error code %lu)",
+ GetLastError())));
+ UnmapViewOfFile(param);
+ CloseHandle(paramHandle);
+ return -1;
+ }
+
+ if (!save_backend_variables(param, client_sock, 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(LOG,
+ (errmsg_internal("could not terminate unstarted process: error code %lu",
+ GetLastError())));
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ UnmapViewOfFile(param);
+ CloseHandle(paramHandle);
+ return -1; /* log made by save_backend_variables */
+ }
+
+ /* Drop the parameter shared memory that is now inherited to the backend */
+ if (!UnmapViewOfFile(param))
+ ereport(LOG,
+ (errmsg("could not unmap view of backend parameter file: error code %lu",
+ GetLastError())));
+ if (!CloseHandle(paramHandle))
+ ereport(LOG,
+ (errmsg("could not close handle to backend parameter file: error code %lu",
+ GetLastError())));
+
+ /*
+ * Reserve the memory region used by our main shared memory segment before
+ * we resume the child process. Normally this should succeed, but if ASLR
+ * is active then it might sometimes fail due to the stack or heap having
+ * gotten mapped into that range. In that case, just terminate the
+ * process and retry.
+ */
+ if (!pgwin32_ReserveSharedMemoryRegion(pi.hProcess))
+ {
+ /* pgwin32_ReserveSharedMemoryRegion already made a log entry */
+ if (!TerminateProcess(pi.hProcess, 255))
+ ereport(LOG,
+ (errmsg_internal("could not terminate process that failed to reserve memory: error code %lu",
+ GetLastError())));
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ if (++retry_count < 100)
+ goto retry;
+ ereport(LOG,
+ (errmsg("giving up after too many tries to reserve shared memory"),
+ errhint("This might be caused by ASLR or antivirus software.")));
+ return -1;
+ }
+
+ /*
+ * 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(LOG,
+ (errmsg_internal("could not terminate unstartable process: error code %lu",
+ GetLastError())));
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return -1;
+ }
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ ereport(LOG,
+ (errmsg_internal("could not resume thread of unstarted process: error code %lu",
+ GetLastError())));
+ return -1;
+ }
+
+ /*
+ * Queue a waiter to signal when this child dies. The wait will be handled
+ * automatically by an operating system thread pool. The memory will be
+ * freed by a later call to waitpid().
+ */
+ childinfo = palloc(sizeof(win32_deadchild_waitinfo));
+ childinfo->procHandle = pi.hProcess;
+ childinfo->procId = pi.dwProcessId;
+
+ if (!RegisterWaitForSingleObject(&childinfo->waitHandle,
+ pi.hProcess,
+ pgwin32_deadchild_callback,
+ childinfo,
+ INFINITE,
+ WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD))
+ ereport(FATAL,
+ (errmsg_internal("could not register process for wait: error code %lu",
+ GetLastError())));
+
+ /* Don't close pi.hProcess here - waitpid() needs access to it */
+
+ 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
+ * dispatch to the appropriate place.
+ *
+ * The first two command line arguments are expected to be "--forkFOO"
+ * (where FOO indicates which postmaster child we are to become), and
+ * the name of a variables file that we can read to load data that would
+ * have been inherited by fork() on Unix. Remaining arguments go to the
+ * subprocess FooMain() routine. XXX
+ */
+void
+SubPostmasterMain(int argc, char *argv[])
+{
+ PostmasterChildType child_type;
+ char *startup_data;
+ size_t startup_data_len;
+
+ /* In EXEC_BACKEND case we will not have inherited these settings */
+ IsPostmasterEnvironment = true;
+ whereToSendOutput = DestNone;
+
+ /* Setup essential subsystems (to ensure elog() behaves sanely) */
+ InitializeGUCOptions();
+
+ /* Check we got appropriate args */
+ if (argc < 3)
+ elog(FATAL, "invalid subpostmaster invocation");
+
+ if (strncmp(argv[1], "--forkchild=", 12) == 0)
+ {
+ char *entry_name = argv[1] + 12;
+ bool found = false;
+
+ for (int idx = 0; idx < lengthof(entry_kinds); idx++)
+ {
+ if (strcmp(entry_kinds[idx].name, entry_name) == 0)
+ {
+ child_type = idx;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ elog(ERROR, "unknown child kind %s", entry_name);
+ }
+
+ /* Read in the variables file */
+ read_backend_variables(argv[2], &startup_data, &startup_data_len);
+
+ /* Setup as postmaster child */
+ InitPostmasterChild(child_type == PMC_SYSLOGGER);
+
+ /*
+ * If appropriate, physically re-attach to shared memory segment. We want
+ * to do this before going any further to ensure that we can attach at the
+ * same address the postmaster used. On the other hand, if we choose not
+ * to re-attach, we may have other cleanup to do.
+ *
+ * If testing EXEC_BACKEND on Linux, you should run this as root before
+ * starting the postmaster:
+ *
+ * sysctl -w kernel.randomize_va_space=0
+ *
+ * This prevents using randomized stack and code addresses that cause the
+ * child process's memory map to be different from the parent's, making it
+ * 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 (entry_kinds[child_type].shmem_attach)
+ PGSharedMemoryReAttach();
+ else
+ PGSharedMemoryNoReAttach();
+
+ /* Read in remaining GUC variables */
+ read_nondefault_variables();
+
+ /*
+ * Check that the data directory looks valid, which will also check the
+ * privileges on the data directory and update our umask and file/group
+ * variables for creating files later. Note: this should really be done
+ * before we create any files or directories.
+ */
+ checkDataDir();
+
+ /*
+ * (re-)read control file, as it contains config. The postmaster will
+ * already have read this, but this process doesn't know about that.
+ */
+ LocalProcessControlFile(false);
+
+ /*
+ * Reload any libraries that were preloaded by the postmaster. Since we
+ * exec'd this process, those libraries didn't come along with us; but we
+ * should load them into all child processes to be consistent with the
+ * non-EXEC_BACKEND behavior.
+ */
+ process_shared_preload_libraries();
+
+ /* Restore basic shared memory pointers */
+ if (UsedShmemSegAddr != NULL)
+ InitShmemAccess(UsedShmemSegAddr);
+
+ /* Run backend or appropriate child */
+ entry_kinds[child_type].entry_fn(startup_data, startup_data_len);
+
+ abort(); /* shouldn't get here */
+}
+#endif /* EXEC_BACKEND */
+
+/* ----------------------------------------------------------------
+ * common process startup code
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * Initialize the basic environment for a postmaster child
+ *
+ * Should be called as early as possible after the child's startup. However,
+ * on EXEC_BACKEND builds it does need to be after read_backend_variables().
+ */
+static void
+InitPostmasterChild(bool am_syslogger)
+{
+ /* Close the postmaster's sockets (as soon as we know them) */
+ ClosePostmasterPorts(am_syslogger);
+
+ IsUnderPostmaster = true; /* we are a postmaster subprocess now */
+
+ /*
+ * 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
+
+ /*
+ * Set reference point for stack-depth checking. This might seem
+ * redundant in !EXEC_BACKEND builds; but it's not because the postmaster
+ * launches its children from signal handlers, so we might be running on
+ * an alternative stack. XXX still true?
+ */
+ (void) set_stack_base();
+
+ InitProcessGlobals();
+
+ /*
+ * make sure stderr is in binary mode before anything can possibly be
+ * written to it, in case it's actually the syslogger pipe, so the pipe
+ * chunking protocol isn't disturbed. Non-logpipe data gets translated on
+ * redirection (e.g. via pg_ctl -l) anyway.
+ */
+#ifdef WIN32
+ _setmode(fileno(stderr), _O_BINARY);
+#endif
+
+ /* We don't want the postmaster's proc_exit() handlers */
+ on_exit_reset();
+
+ /* In EXEC_BACKEND case we will not have inherited BlockSig etc values */
+#ifdef EXEC_BACKEND
+ pqinitmask();
+#endif
+
+ /* Initialize process-local latch support */
+ InitializeLatchSupport();
+ InitProcessLocalLatch();
+ InitializeLatchWaitSet();
+
+ /*
+ * If possible, make this process a group leader, so that the postmaster
+ * can signal any child processes too. Not all processes will have
+ * children, but for consistency we make all postmaster child processes do
+ * this.
+ */
+#ifdef HAVE_SETSID
+ if (setsid() < 0)
+ elog(FATAL, "setsid() failed: %m");
+#endif
+
+ /*
+ * Every postmaster child process is expected to respond promptly to
+ * SIGQUIT at all times. Therefore we centrally remove SIGQUIT from
+ * BlockSig and install a suitable signal handler. (Client-facing
+ * processes may choose to replace this default choice of handler with
+ * quickdie().) All other blockable signals remain blocked for now.
+ */
+ pqsignal(SIGQUIT, SignalHandlerForCrashExit);
+
+ sigdelset(&BlockSig, SIGQUIT);
+ sigprocmask(SIG_SETMASK, &BlockSig, NULL);
+
+ /* Request a signal if the postmaster dies, if possible. */
+ PostmasterDeathSignalInit();
+
+ /* Don't give the pipe to subprograms that we execute. */
+#ifndef WIN32
+ if (fcntl(postmaster_alive_fds[POSTMASTER_FD_WATCH], F_SETFD, FD_CLOEXEC) < 0)
+ ereport(FATAL,
+ (errcode_for_socket_access(),
+ errmsg_internal("could not set postmaster death monitoring pipe to FD_CLOEXEC mode: %m")));
+#endif
+}
+
+/*
+ * Initialize the basic environment for a standalone process.
+ *
+ * argv0 has to be suitable to find the program's executable.
+ */
+void
+InitStandaloneProcess(const char *argv0)
+{
+ Assert(!IsPostmasterEnvironment);
+
+ MyBackendType = B_STANDALONE_BACKEND;
+
+ /*
+ * Start our win32 signal implementation
+ */
+#ifdef WIN32
+ pgwin32_signal_initialize();
+#endif
+
+ InitProcessGlobals();
+
+ /* Initialize process-local latch support */
+ InitializeLatchSupport();
+ InitProcessLocalLatch();
+ InitializeLatchWaitSet();
+
+ /*
+ * For consistency with InitPostmasterChild, initialize signal mask here.
+ * But we don't unblock SIGQUIT or provide a default handler for it.
+ */
+ pqinitmask();
+ sigprocmask(SIG_SETMASK, &BlockSig, NULL);
+
+ /* Compute paths, no postmaster to inherit from */
+ if (my_exec_path[0] == '\0')
+ {
+ if (find_my_exec(argv0, my_exec_path) < 0)
+ elog(FATAL, "%s: could not locate my own executable path",
+ argv0);
+ }
+
+ if (pkglib_path[0] == '\0')
+ get_pkglib_path(my_exec_path, pkglib_path);
+}
+
+
+#ifdef EXEC_BACKEND
+
+/*
+ * The following need to be available to the save/restore_backend_variables
+ * functions. They are marked NON_EXEC_STATIC in their home modules.
+ */
+extern slock_t *ShmemLock;
+extern slock_t *ProcStructLock;
+extern PGPROC *AuxiliaryProcs;
+extern PMSignalData *PMSignalState;
+extern pg_time_t first_syslogger_file_time;
+extern struct bkend *ShmemBackendArray;
+extern bool redirection_done;
+
+#ifndef WIN32
+#define write_inheritable_socket(dest, src, childpid) ((*(dest) = (src)), true)
+#define read_inheritable_socket(dest, src) (*(dest) = *(src))
+#else
+static bool write_duplicated_handle(HANDLE *dest, HANDLE src, HANDLE child);
+static bool write_inheritable_socket(InheritableSocket *dest, SOCKET src,
+ pid_t childPid);
+static void read_inheritable_socket(SOCKET *dest, InheritableSocket *src);
+#endif
+
+
+/* Save critical backend variables into the BackendParameters struct */
+#ifndef WIN32
+static bool
+save_backend_variables(BackendParameters *param, ClientSocket *client_sock)
+#else
+static bool
+save_backend_variables(BackendParameters *param, ClientSocket *client_sock,
+ HANDLE childProcess, pid_t childPid)
+#endif
+{
+ if (client_sock)
+ memcpy(¶m->client_sock, client_sock, sizeof(ClientSocket));
+ else
+ memset(¶m->client_sock, 0, sizeof(ClientSocket));
+ if (!write_inheritable_socket(¶m->inh_sock,
+ client_sock ? client_sock->sock : PGINVALID_SOCKET,
+ childPid))
+ return false;
+
+ strlcpy(param->DataDir, DataDir, MAXPGPATH);
+
+ param->MyCancelKey = MyCancelKey;
+ param->MyPMChildSlot = MyPMChildSlot;
+
+#ifdef WIN32
+ param->ShmemProtectiveRegion = ShmemProtectiveRegion;
+#endif
+ param->UsedShmemSegID = UsedShmemSegID;
+ param->UsedShmemSegAddr = UsedShmemSegAddr;
+
+ param->ShmemLock = ShmemLock;
+ param->ShmemVariableCache = ShmemVariableCache;
+ param->ShmemBackendArray = ShmemBackendArray;
+
+#ifndef HAVE_SPINLOCKS
+ param->SpinlockSemaArray = SpinlockSemaArray;
+#endif
+ param->NamedLWLockTrancheRequests = NamedLWLockTrancheRequests;
+ param->NamedLWLockTrancheArray = NamedLWLockTrancheArray;
+ param->MainLWLockArray = MainLWLockArray;
+ param->ProcStructLock = ProcStructLock;
+ param->ProcGlobal = ProcGlobal;
+ param->AuxiliaryProcs = AuxiliaryProcs;
+ param->PreparedXactProcs = PreparedXactProcs;
+ param->PMSignalState = PMSignalState;
+
+ param->PostmasterPid = PostmasterPid;
+ param->PgStartTime = PgStartTime;
+ param->PgReloadTime = PgReloadTime;
+ param->first_syslogger_file_time = first_syslogger_file_time;
+
+ param->redirection_done = redirection_done;
+ param->IsBinaryUpgrade = IsBinaryUpgrade;
+ param->query_id_enabled = query_id_enabled;
+ param->max_safe_fds = max_safe_fds;
+
+ param->MaxBackends = MaxBackends;
+
+#ifdef WIN32
+ param->PostmasterHandle = PostmasterHandle;
+ if (!write_duplicated_handle(¶m->initial_signal_pipe,
+ pgwin32_create_signal_listener(childPid),
+ childProcess))
+ return false;
+#else
+ memcpy(¶m->postmaster_alive_fds, &postmaster_alive_fds,
+ sizeof(postmaster_alive_fds));
+#endif
+
+ memcpy(¶m->syslogPipe, &syslogPipe, sizeof(syslogPipe));
+
+ strlcpy(param->my_exec_path, my_exec_path, MAXPGPATH);
+
+ strlcpy(param->pkglib_path, pkglib_path, MAXPGPATH);
+
+ return true;
+}
+
+
+#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 bool
+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(LOG,
+ (errmsg_internal("could not duplicate handle to be written to backend parameter file: error code %lu",
+ GetLastError())));
+ return false;
+ }
+
+ *dest = hChild;
+ return true;
+}
+
+/*
+ * 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 bool
+write_inheritable_socket(InheritableSocket *dest, SOCKET src, pid_t childpid)
+{
+ dest->origsocket = src;
+ if (src != 0 && src != PGINVALID_SOCKET)
+ {
+ /* Actual socket */
+ if (WSADuplicateSocket(src, childpid, &dest->wsainfo) != 0)
+ {
+ ereport(LOG,
+ (errmsg("could not duplicate socket %d for use in backend: error code %d",
+ (int) src, WSAGetLastError())));
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ * Read a duplicate socket structure back, and get the socket descriptor.
+ */
+static void
+read_inheritable_socket(SOCKET *dest, InheritableSocket *src)
+{
+ SOCKET s;
+
+ if (src->origsocket == PGINVALID_SOCKET || src->origsocket == 0)
+ {
+ /* 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;
+
+ /*
+ * 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 *id, char **startup_data, size_t *startup_data_len)
+{
+ BackendParameters param;
+
+#ifndef WIN32
+ /* Non-win32 implementation reads from file */
+ FILE *fp;
+
+ /* Open file */
+ fp = AllocateFile(id, PG_BINARY_R);
+ if (!fp)
+ {
+ write_stderr("could not open backend variables file \"%s\": %s\n",
+ id, strerror(errno));
+ exit(1);
+ }
+
+ if (fread(¶m, sizeof(param), 1, fp) != 1)
+ {
+ write_stderr("could not read from backend variables file \"%s\": %s\n",
+ id, strerror(errno));
+ exit(1);
+ }
+
+ /* read startup data */
+ if (fread((char *) startup_data_len, sizeof(size_t), 1, fp) != 1)
+ {
+ write_stderr("could not read len from backend variables file \"%s\": %s\n",
+ id, strerror(errno));
+ exit(1);
+ }
+ if (*startup_data_len > 0)
+ {
+ *startup_data = palloc(*startup_data_len);
+ if (fread(*startup_data, *startup_data_len, 1, fp) != 1)
+ {
+ write_stderr("could not read startup data from backend variables file \"%s\": %s\n",
+ id, strerror(errno));
+ exit(1);
+ }
+ }
+ else
+ *startup_data = NULL;
+
+ /* Release file */
+ FreeFile(fp);
+ if (unlink(id) != 0)
+ {
+ write_stderr("could not remove file \"%s\": %s\n",
+ id, strerror(errno));
+ exit(1);
+ }
+#else
+ /* Win32 version uses mapped file */
+ HANDLE paramHandle;
+ BackendParameters *paramp;
+
+#ifdef _WIN64
+ paramHandle = (HANDLE) _atoi64(id);
+#else
+ paramHandle = (HANDLE) atol(id);
+#endif
+ paramp = MapViewOfFile(paramHandle, FILE_MAP_READ, 0, 0, 0);
+ if (!paramp)
+ {
+ write_stderr("could not map view of backend variables: error code %lu\n",
+ GetLastError());
+ exit(1);
+ }
+
+ memcpy(¶m, paramp, sizeof(BackendParameters));
+
+ /* XXX: read startup data */
+
+ if (!UnmapViewOfFile(paramp))
+ {
+ write_stderr("could not unmap view of backend variables: error code %lu\n",
+ GetLastError());
+ exit(1);
+ }
+
+ if (!CloseHandle(paramHandle))
+ {
+ write_stderr("could not close handle to backend parameter variables: error code %lu\n",
+ GetLastError());
+ exit(1);
+ }
+#endif
+
+ restore_backend_variables(¶m);
+}
+
+/* Restore critical backend variables from the BackendParameters struct */
+static void
+restore_backend_variables(BackendParameters *param)
+{
+ if (param->client_sock.sock != PGINVALID_SOCKET)
+ {
+ MyClientSocket = MemoryContextAlloc(TopMemoryContext, sizeof(ClientSocket));
+ memcpy(MyClientSocket, ¶m->client_sock, sizeof(ClientSocket));
+ read_inheritable_socket(&MyClientSocket->sock, ¶m->inh_sock);
+ }
+
+ SetDataDir(param->DataDir);
+
+ MyCancelKey = param->MyCancelKey;
+ MyPMChildSlot = param->MyPMChildSlot;
+
+#ifdef WIN32
+ ShmemProtectiveRegion = param->ShmemProtectiveRegion;
+#endif
+ UsedShmemSegID = param->UsedShmemSegID;
+ UsedShmemSegAddr = param->UsedShmemSegAddr;
+
+ ShmemLock = param->ShmemLock;
+ ShmemVariableCache = param->ShmemVariableCache;
+ ShmemBackendArray = param->ShmemBackendArray;
+
+#ifndef HAVE_SPINLOCKS
+ SpinlockSemaArray = param->SpinlockSemaArray;
+#endif
+ NamedLWLockTrancheRequests = param->NamedLWLockTrancheRequests;
+ NamedLWLockTrancheArray = param->NamedLWLockTrancheArray;
+ MainLWLockArray = param->MainLWLockArray;
+ ProcStructLock = param->ProcStructLock;
+ ProcGlobal = param->ProcGlobal;
+ AuxiliaryProcs = param->AuxiliaryProcs;
+ PreparedXactProcs = param->PreparedXactProcs;
+ PMSignalState = param->PMSignalState;
+
+ PostmasterPid = param->PostmasterPid;
+ PgStartTime = param->PgStartTime;
+ PgReloadTime = param->PgReloadTime;
+ first_syslogger_file_time = param->first_syslogger_file_time;
+
+ redirection_done = param->redirection_done;
+ IsBinaryUpgrade = param->IsBinaryUpgrade;
+ query_id_enabled = param->query_id_enabled;
+ max_safe_fds = param->max_safe_fds;
+
+ MaxBackends = param->MaxBackends;
+
+#ifdef WIN32
+ PostmasterHandle = param->PostmasterHandle;
+ pgwin32_initial_signal_pipe = param->initial_signal_pipe;
+#else
+ memcpy(&postmaster_alive_fds, ¶m->postmaster_alive_fds,
+ sizeof(postmaster_alive_fds));
+#endif
+
+ memcpy(&syslogPipe, ¶m->syslogPipe, sizeof(syslogPipe));
+
+ strlcpy(my_exec_path, param->my_exec_path, MAXPGPATH);
+
+ strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
+
+ /*
+ * We need to restore fd.c's counts of externally-opened FDs; to avoid
+ * confusion, be sure to do this after restoring max_safe_fds. (Note:
+ * BackendInitialize will handle this for client_sock->sock.)
+ */
+#ifndef WIN32
+ if (postmaster_alive_fds[0] >= 0)
+ ReserveExternalFD();
+ if (postmaster_alive_fds[1] >= 0)
+ ReserveExternalFD();
+#endif
+}
+
+#endif /* EXEC_BACKEND */
diff --git a/src/backend/postmaster/meson.build b/src/backend/postmaster/meson.build
index cda921fd10b..89ff11beb0a 100644
--- a/src/backend/postmaster/meson.build
+++ b/src/backend/postmaster/meson.build
@@ -6,8 +6,8 @@ backend_sources += files(
'bgworker.c',
'bgwriter.c',
'checkpointer.c',
- 'fork_process.c',
'interrupt.c',
+ 'launch_backend.c',
'pgarch.c',
'postmaster.c',
'startup.c',
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 46af3495644..b82d66fab52 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -36,6 +36,7 @@
#include "lib/binaryheap.h"
#include "libpq/pqsignal.h"
#include "pgstat.h"
+#include "postmaster/auxprocess.h"
#include "postmaster/interrupt.h"
#include "postmaster/pgarch.h"
#include "storage/fd.h"
@@ -211,8 +212,14 @@ PgArchCanRestart(void)
/* Main entry point for archiver process */
void
-PgArchiverMain(void)
+PgArchiverMain(char *startup_data, size_t startup_data_len)
{
+ Assert(startup_data_len == 0);
+
+ MyAuxProcType = ArchiverProcess;
+ MyBackendType = B_ARCHIVER;
+ AuxiliaryProcessInit();
+
/*
* Ignore all signals usually bound to some action in the postmaster,
* except for SIGHUP, SIGTERM, SIGUSR1, SIGUSR2, and SIGQUIT.
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index b73ba983f3e..dc5cac19878 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -109,7 +109,6 @@
#include "postmaster/autovacuum.h"
#include "postmaster/auxprocess.h"
#include "postmaster/bgworker_internals.h"
-#include "postmaster/fork_process.h"
#include "postmaster/interrupt.h"
#include "postmaster/pgarch.h"
#include "postmaster/postmaster.h"
@@ -131,10 +130,6 @@
#include "utils/timestamp.h"
#include "utils/varlena.h"
-#ifdef EXEC_BACKEND
-#include "storage/spin.h"
-#endif
-
/*
* Possible types of a backend. Beyond being the possible bkend_type values in
@@ -187,7 +182,7 @@ typedef struct bkend
static dlist_head BackendList = DLIST_STATIC_INIT(BackendList);
#ifdef EXEC_BACKEND
-static Backend *ShmemBackendArray;
+Backend *ShmemBackendArray;
#endif
BackgroundWorker *MyBgworkerEntry = NULL;
@@ -412,7 +407,7 @@ static void StartupPacketTimeoutHandler(void);
static void CleanupBackend(int pid, int exitstatus);
static bool CleanupBackgroundWorker(int pid, int exitstatus);
static void HandleChildCrash(int pid, int exitstatus, const char *procname);
-static void LogChildExit(int lev, const char *procname,
+static void LogChildExit(int lev, const char *procnamBackendInitializee,
int pid, int exitstatus);
static void PostmasterStateMachine(void);
@@ -427,7 +422,6 @@ typedef enum CAC_state
} CAC_state;
static void BackendInitialize(ClientSocket *client_sock, CAC_state cac);
-static void BackendRun(void) pg_attribute_noreturn();
static void ExitPostmaster(int status) pg_attribute_noreturn();
static int ServerLoop(void);
static int BackendStartup(ClientSocket *port);
@@ -448,7 +442,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(PostmasterChildType type);
static void StartAutovacuumWorker(void);
static void MaybeStartWalReceiver(void);
static void InitPostmasterDeathWatchHandle(void);
@@ -483,95 +477,19 @@ typedef struct
} win32_deadchild_waitinfo;
#endif /* WIN32 */
-static pid_t backend_forkexec(ClientSocket *client_sock, CAC_state cac);
-static pid_t internal_forkexec(int argc, char *argv[], ClientSocket *client_sock);
-
-/* Type for a socket that can be inherited to a client process */
-#ifdef WIN32
-typedef struct
-{
- SOCKET origsocket; /* Original socket value, or PGINVALID_SOCKET
- * if not a socket */
- WSAPROTOCOL_INFO wsainfo;
-} InheritableSocket;
-#else
-typedef int InheritableSocket;
-#endif
-
-/*
- * Structure contains all variables passed to exec:ed backends
- */
-typedef struct
-{
- ClientSocket client_sock;
- InheritableSocket serialized_sock;
- char DataDir[MAXPGPATH];
- int32 MyCancelKey;
- int MyPMChildSlot;
-#ifndef WIN32
- unsigned long UsedShmemSegID;
-#else
- void *ShmemProtectiveRegion;
- HANDLE UsedShmemSegID;
-#endif
- void *UsedShmemSegAddr;
- slock_t *ShmemLock;
- VariableCache ShmemVariableCache;
- Backend *ShmemBackendArray;
-#ifndef HAVE_SPINLOCKS
- PGSemaphore *SpinlockSemaArray;
-#endif
- int NamedLWLockTrancheRequests;
- NamedLWLockTranche *NamedLWLockTrancheArray;
- LWLockPadded *MainLWLockArray;
- slock_t *ProcStructLock;
- PROC_HDR *ProcGlobal;
- PGPROC *AuxiliaryProcs;
- PGPROC *PreparedXactProcs;
- PMSignalData *PMSignalState;
- pid_t PostmasterPid;
- TimestampTz PgStartTime;
- TimestampTz PgReloadTime;
- pg_time_t first_syslogger_file_time;
- bool redirection_done;
- bool IsBinaryUpgrade;
- bool query_id_enabled;
- int max_safe_fds;
- int MaxBackends;
-#ifdef WIN32
- HANDLE PostmasterHandle;
- HANDLE initial_signal_pipe;
- HANDLE syslogPipe[2];
-#else
- int postmaster_alive_fds[2];
- int syslogPipe[2];
-#endif
- char my_exec_path[MAXPGPATH];
- char pkglib_path[MAXPGPATH];
-
- BackgroundWorker MyBgworkerEntry;
-} BackendParameters;
-
-static void read_backend_variables(char *id, ClientSocket *client_sock);
-static void restore_backend_variables(BackendParameters *param, ClientSocket *client_sock);
-
-#ifndef WIN32
-static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock);
-#else
-static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock,
- HANDLE childProcess, pid_t childPid);
-#endif
-
static void ShmemBackendArrayAdd(Backend *bn);
static void ShmemBackendArrayRemove(Backend *bn);
#endif /* EXEC_BACKEND */
-#define StartupDataBase() StartChildProcess(StartupProcess)
-#define StartArchiver() StartChildProcess(ArchiverProcess)
-#define StartBackgroundWriter() StartChildProcess(BgWriterProcess)
-#define StartCheckpointer() StartChildProcess(CheckpointerProcess)
-#define StartWalWriter() StartChildProcess(WalWriterProcess)
-#define StartWalReceiver() StartChildProcess(WalReceiverProcess)
+#define StartupDataBase() StartChildProcess(PMC_STARTUP)
+#define StartArchiver() StartChildProcess(PMC_ARCHIVER)
+#define StartBackgroundWriter() StartChildProcess(PMC_BGWRITER)
+#define StartCheckpointer() StartChildProcess(PMC_CHECKPOINTER)
+#define StartWalWriter() StartChildProcess(PMC_WAL_WRITER)
+#define StartWalReceiver() StartChildProcess(PMC_WAL_RECEIVER)
+
+#define StartAutoVacLauncher() StartChildProcess(PMC_AV_LAUNCHER);
+#define StartAutoVacWorker() StartChildProcess(PMC_AV_WORKER);
/* Macros to check exit status of a child process */
#define EXIT_STATUS_0(st) ((st) == 0)
@@ -1115,11 +1033,11 @@ PostmasterMain(int argc, char *argv[])
/*
* Clean out the temp directory used to transmit parameters to child
- * processes (see internal_forkexec, below). We must do this before
- * launching any child processes, else we have a race condition: we could
- * remove a parameter file before the child can read it. It should be
- * safe to do so now, because we verified earlier that there are no
- * conflicting Postgres processes in this data directory.
+ * processes (see internal_forkexec). We must do this before launching
+ * any child processes, else we have a race condition: we could remove a
+ * parameter file before the child can read it. It should be safe to do
+ * so now, because we verified earlier that there are no conflicting
+ * Postgres processes in this data directory.
*/
RemovePgTempFilesInDir(PG_TEMP_FILES_DIR, true, false);
#endif
@@ -4029,6 +3947,11 @@ TerminateChildren(int signal)
signal_child(PgArchPID, signal);
}
+typedef struct BackendStartupInfo
+{
+ bool canAcceptConnections;
+} BackendStartupInfo;
+
/*
* BackendStartup -- start backend process
*
@@ -4041,7 +3964,7 @@ BackendStartup(ClientSocket *client_sock)
{
Backend *bn; /* for backend cleanup */
pid_t pid;
- CAC_state cac;
+ BackendStartupInfo info;
/*
* Create backend data structure. Better before the fork() so we can
@@ -4070,11 +3993,9 @@ BackendStartup(ClientSocket *client_sock)
return STATUS_ERROR;
}
- bn->cancel_key = MyCancelKey;
-
/* Pass down canAcceptConnections state */
- cac = canAcceptConnections(BACKEND_TYPE_NORMAL);
- bn->dead_end = (cac != CAC_OK);
+ info.canAcceptConnections = canAcceptConnections(BACKEND_TYPE_NORMAL);
+ bn->cancel_key = MyCancelKey;
/*
* Unless it's a dead_end child, assign it a child slot number
@@ -4087,26 +4008,7 @@ BackendStartup(ClientSocket *client_sock)
/* Hasn't asked to be notified about any bgworkers yet */
bn->bgworker_notify = false;
-#ifdef EXEC_BACKEND
- pid = backend_forkexec(client_sock, cac);
-#else /* !EXEC_BACKEND */
- pid = fork_process();
- if (pid == 0) /* child */
- {
- /* Detangle from postmaster */
- InitPostmasterChild();
-
- /* Close the postmaster's sockets */
- ClosePostmasterPorts(false);
-
- /* Perform additional initialization and collect startup packet */
- BackendInitialize(client_sock, cac);
-
- /* And run the backend */
- BackendRun();
- }
-#endif /* EXEC_BACKEND */
-
+ pid = postmaster_child_launch(PMC_BACKEND, (char *) &info, sizeof(info), client_sock);
if (pid < 0)
{
/* in parent, fork failed */
@@ -4143,6 +4045,63 @@ BackendStartup(ClientSocket *client_sock)
return STATUS_OK;
}
+void
+BackendMain(char *startup_data, size_t startup_data_len)
+{
+ BackendStartupInfo *info = (BackendStartupInfo *) startup_data;
+
+ Assert(startup_data_len == sizeof(BackendStartupInfo));
+ Assert(MyClientSocket != NULL);
+
+#ifdef EXEC_BACKEND
+
+ /*
+ * Need to reinitialize the SSL library in the backend, since the context
+ * structures contain function pointers and cannot be passed through the
+ * parameter file.
+ *
+ * If for some reason reload fails (maybe the user installed broken key
+ * files), soldier on without SSL; that's better than all connections
+ * becoming impossible.
+ *
+ * XXX should we do this in all child processes? For the moment it's
+ * enough to do it in backend children.
+ */
+#ifdef USE_SSL
+ if (EnableSSL)
+ {
+ if (secure_initialize(false) == 0)
+ LoadedSSL = true;
+ else
+ ereport(LOG,
+ (errmsg("SSL configuration could not be loaded in child process")));
+ }
+#endif
+#endif
+
+ /* Perform additional initialization and collect startup packet */
+ BackendInitialize(MyClientSocket, info->canAcceptConnections);
+
+ /*
+ * Create a per-backend PGPROC struct in shared memory. We must do this
+ * before we can use LWLocks. In the !EXEC_BACKEND case (here) this could
+ * be delayed a bit further, but EXEC_BACKEND needs to do stuff with
+ * LWLocks before PostgresMain(), so we do it here as well for symmetry.
+ */
+ InitProcess();
+
+ /* Attach process to shared data structures */
+ AttachSharedMemoryAndSemaphores();
+
+ /*
+ * Make sure we aren't in PostmasterContext anymore. (We can't delete it
+ * just yet, though, because InitPostgres will need the HBA data.)
+ */
+ MemoryContextSwitchTo(TopMemoryContext);
+
+ PostgresMain(MyProcPort->database_name, MyProcPort->user_name);
+}
+
/*
* Try to report backend fork() failure to client before we close the
* connection. Since we do not care to risk blocking the postmaster on
@@ -4414,732 +4373,161 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac)
/*
- * BackendRun -- set up the backend's argument list and invoke PostgresMain()
+ * ExitPostmaster -- cleanup
*
- * returns:
- * Doesn't return at all.
+ * Do NOT call exit() directly --- always go through here!
*/
static void
-BackendRun(void)
+ExitPostmaster(int status)
{
+#ifdef HAVE_PTHREAD_IS_THREADED_NP
+
/*
- * Create a per-backend PGPROC struct in shared memory. We must do this
- * before we can use LWLocks (in AttachSharedMemoryAndSemaphores).
+ * There is no known cause for a postmaster to become multithreaded after
+ * startup. Recheck to account for the possibility of unknown causes.
+ * This message uses LOG level, because an unclean shutdown at this point
+ * would usually not look much different from a clean shutdown.
*/
- InitProcess();
+ if (pthread_is_threaded_np() != 0)
+ ereport(LOG,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg_internal("postmaster became multithreaded"),
+ errdetail("Please report this to <%s>.", PACKAGE_BUGREPORT)));
+#endif
- /* Attach process to shared data structures */
- AttachSharedMemoryAndSemaphores();
+ /* should cleanup shared memory and kill all backends */
/*
- * Make sure we aren't in PostmasterContext anymore. (We can't delete it
- * just yet, though, because InitPostgres will need the HBA data.)
+ * Not sure of the semantics here. When the Postmaster dies, should the
+ * backends all be killed? probably not.
+ *
+ * MUST -- vadim 05-10-1999
*/
- MemoryContextSwitchTo(TopMemoryContext);
-
- PostgresMain(MyProcPort->database_name, MyProcPort->user_name);
-}
-
-
-#ifdef EXEC_BACKEND
-
-/*
- * postmaster_forkexec -- fork and exec a postmaster subprocess
- *
- * The caller must have set up the argv array already, except for argv[2]
- * which will be filled with the name of the temp variable file.
- *
- * Returns the child process PID, or -1 on fork failure (a suitable error
- * message has been logged on failure).
- *
- * All uses of this routine will dispatch to SubPostmasterMain in the
- * child process.
- */
-pid_t
-postmaster_forkexec(int argc, char *argv[])
-{
- ClientSocket client_sock;
-
- /* This entry point doesn't pass a client socket */
- memset(&client_sock, 0, sizeof(ClientSocket));
- return internal_forkexec(argc, argv, &client_sock);
-}
-
-/*
- * 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(ClientSocket *client_sock, CAC_state cac)
-{
- char *av[5];
- int ac = 0;
- char cacbuf[10];
- av[ac++] = "postgres";
- av[ac++] = "--forkbackend";
- av[ac++] = NULL; /* filled in by internal_forkexec */
-
- snprintf(cacbuf, sizeof(cacbuf), "%d", (int) cac);
- av[ac++] = cacbuf;
-
- av[ac] = NULL;
- Assert(ac < lengthof(av));
-
- return internal_forkexec(ac, av, client_sock);
+ proc_exit(status);
}
-#ifndef WIN32
-
/*
- * internal_forkexec non-win32 implementation
- *
- * - writes out backend variables to the parameter file
- * - fork():s, and then exec():s the child process
+ * Handle pmsignal conditions representing requests from backends,
+ * and check for promote and logrotate requests from pg_ctl.
*/
-static pid_t
-internal_forkexec(int argc, char *argv[], ClientSocket *client_sock)
+static void
+process_pm_pmsignal(void)
{
- static unsigned long tmpBackendFileNum = 0;
- pid_t pid;
- char tmpfilename[MAXPGPATH];
- BackendParameters param;
- FILE *fp;
-
- if (!save_backend_variables(¶m, client_sock))
- return -1; /* log made by save_backend_variables */
+ pending_pm_pmsignal = false;
- /* Calculate name for temp file */
- snprintf(tmpfilename, MAXPGPATH, "%s/%s.backend_var.%d.%lu",
- PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX,
- MyProcPid, ++tmpBackendFileNum);
+ ereport(DEBUG2,
+ (errmsg_internal("postmaster received pmsignal signal")));
- /* Open file */
- fp = AllocateFile(tmpfilename, PG_BINARY_W);
- if (!fp)
+ /*
+ * RECOVERY_STARTED and BEGIN_HOT_STANDBY signals are ignored in
+ * unexpected states. If the startup process quickly starts up, completes
+ * recovery, exits, we might process the death of the startup process
+ * first. We don't want to go back to recovery in that case.
+ */
+ if (CheckPostmasterSignal(PMSIGNAL_RECOVERY_STARTED) &&
+ pmState == PM_STARTUP && Shutdown == NoShutdown)
{
+ /* WAL redo has started. We're out of reinitialization. */
+ FatalError = false;
+ AbortStartTime = 0;
+
/*
- * As in OpenTemporaryFileInTablespace, try to make the temp-file
- * directory, ignoring errors.
+ * Start the archiver if we're responsible for (re-)archiving received
+ * files.
*/
- (void) MakePGDirectory(PG_TEMP_FILES_DIR);
+ Assert(PgArchPID == 0);
+ if (XLogArchivingAlways())
+ PgArchPID = StartArchiver();
- fp = AllocateFile(tmpfilename, PG_BINARY_W);
- if (!fp)
+ /*
+ * If we aren't planning to enter hot standby mode later, treat
+ * RECOVERY_STARTED as meaning we're out of startup, and report status
+ * accordingly.
+ */
+ if (!EnableHotStandby)
{
- ereport(LOG,
- (errcode_for_file_access(),
- errmsg("could not create file \"%s\": %m",
- tmpfilename)));
- return -1;
+ AddToDataDirLockFile(LOCK_FILE_LINE_PM_STATUS, PM_STATUS_STANDBY);
+#ifdef USE_SYSTEMD
+ sd_notify(0, "READY=1");
+#endif
}
+
+ pmState = PM_RECOVERY;
}
- if (fwrite(¶m, sizeof(param), 1, fp) != 1)
+ if (CheckPostmasterSignal(PMSIGNAL_BEGIN_HOT_STANDBY) &&
+ pmState == PM_RECOVERY && Shutdown == NoShutdown)
{
ereport(LOG,
- (errcode_for_file_access(),
- errmsg("could not write to file \"%s\": %m", tmpfilename)));
- FreeFile(fp);
- return -1;
+ (errmsg("database system is ready to accept read-only connections")));
+
+ /* Report status */
+ AddToDataDirLockFile(LOCK_FILE_LINE_PM_STATUS, PM_STATUS_READY);
+#ifdef USE_SYSTEMD
+ sd_notify(0, "READY=1");
+#endif
+
+ pmState = PM_HOT_STANDBY;
+ connsAllowed = true;
+
+ /* Some workers may be scheduled to start now */
+ StartWorkerNeeded = true;
}
- /* Release file */
- if (FreeFile(fp))
+ /* Process background worker state changes. */
+ if (CheckPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE))
{
- ereport(LOG,
- (errcode_for_file_access(),
- errmsg("could not write to file \"%s\": %m", tmpfilename)));
- return -1;
+ /* Accept new worker requests only if not stopping. */
+ BackgroundWorkerStateChange(pmState < PM_STOP_BACKENDS);
+ StartWorkerNeeded = true;
}
- /* Make sure caller set up argv properly */
- Assert(argc >= 3);
- Assert(argv[argc] == NULL);
- Assert(strncmp(argv[1], "--fork", 6) == 0);
- Assert(argv[2] == NULL);
-
- /* Insert temp file name after --fork argument */
- argv[2] = tmpfilename;
+ if (StartWorkerNeeded || HaveCrashedWorker)
+ maybe_start_bgworkers();
- /* Fire off execv in child */
- if ((pid = fork_process()) == 0)
+ /* Tell syslogger to rotate logfile if requested */
+ if (SysLoggerPID != 0)
{
- if (execv(postgres_exec_path, argv) < 0)
+ if (CheckLogrotateSignal())
{
- ereport(LOG,
- (errmsg("could not execute server process \"%s\": %m",
- postgres_exec_path)));
- /* We're already in the child process here, can't return */
- exit(1);
+ signal_child(SysLoggerPID, SIGUSR1);
+ RemoveLogrotateSignalFiles();
+ }
+ else if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE))
+ {
+ signal_child(SysLoggerPID, SIGUSR1);
}
}
- 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)
-{
- int retry_count = 0;
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- int i;
- int j;
- char cmdLine[MAXPGPATH * 2];
- HANDLE paramHandle;
- BackendParameters *param;
- SECURITY_ATTRIBUTES sa;
- char paramHandleStr[32];
- win32_deadchild_waitinfo *childinfo;
-
- /* Make sure caller set up argv properly */
- Assert(argc >= 3);
- Assert(argv[argc] == NULL);
- Assert(strncmp(argv[1], "--fork", 6) == 0);
- Assert(argv[2] == NULL);
-
- /* Resume here if we need to retry */
-retry:
-
- /* 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)
+ if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER) &&
+ Shutdown <= SmartShutdown && pmState < PM_STOP_BACKENDS)
{
- ereport(LOG,
- (errmsg("could not create backend parameter file mapping: error code %lu",
- GetLastError())));
- return -1;
+ /*
+ * Start one iteration of the autovacuum daemon, even if autovacuuming
+ * is nominally not enabled. This is so we can have an active defense
+ * against transaction ID wraparound. We set a flag for the main loop
+ * to do it rather than trying to do it here --- this is because the
+ * autovac process itself may send the signal, and we want to handle
+ * that by launching another iteration as soon as the current one
+ * completes.
+ */
+ start_autovac_launcher = true;
}
- param = MapViewOfFile(paramHandle, FILE_MAP_WRITE, 0, 0, sizeof(BackendParameters));
- if (!param)
+ if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER) &&
+ Shutdown <= SmartShutdown && pmState < PM_STOP_BACKENDS)
{
- ereport(LOG,
- (errmsg("could not map backend parameter memory: error code %lu",
- GetLastError())));
- CloseHandle(paramHandle);
- return -1;
+ /* The autovacuum launcher wants us to start a worker process. */
+ StartAutovacuumWorker();
}
- /* Insert temp file name after --fork argument */
-#ifdef _WIN64
- sprintf(paramHandleStr, "%llu", (LONG_PTR) paramHandle);
-#else
- sprintf(paramHandleStr, "%lu", (DWORD) paramHandle);
-#endif
- 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')
+ if (CheckPostmasterSignal(PMSIGNAL_START_WALRECEIVER))
{
- ereport(LOG,
- (errmsg("subprocess command line too long")));
- UnmapViewOfFile(param);
- CloseHandle(paramHandle);
- 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))
- {
- ereport(LOG,
- (errmsg("CreateProcess() call failed: %m (error code %lu)",
- GetLastError())));
- UnmapViewOfFile(param);
- CloseHandle(paramHandle);
- 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(LOG,
- (errmsg_internal("could not terminate unstarted process: error code %lu",
- GetLastError())));
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
- UnmapViewOfFile(param);
- CloseHandle(paramHandle);
- return -1; /* log made by save_backend_variables */
- }
-
- /* Drop the parameter shared memory that is now inherited to the backend */
- if (!UnmapViewOfFile(param))
- ereport(LOG,
- (errmsg("could not unmap view of backend parameter file: error code %lu",
- GetLastError())));
- if (!CloseHandle(paramHandle))
- ereport(LOG,
- (errmsg("could not close handle to backend parameter file: error code %lu",
- GetLastError())));
-
- /*
- * Reserve the memory region used by our main shared memory segment before
- * we resume the child process. Normally this should succeed, but if ASLR
- * is active then it might sometimes fail due to the stack or heap having
- * gotten mapped into that range. In that case, just terminate the
- * process and retry.
- */
- if (!pgwin32_ReserveSharedMemoryRegion(pi.hProcess))
- {
- /* pgwin32_ReserveSharedMemoryRegion already made a log entry */
- if (!TerminateProcess(pi.hProcess, 255))
- ereport(LOG,
- (errmsg_internal("could not terminate process that failed to reserve memory: error code %lu",
- GetLastError())));
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
- if (++retry_count < 100)
- goto retry;
- ereport(LOG,
- (errmsg("giving up after too many tries to reserve shared memory"),
- errhint("This might be caused by ASLR or antivirus software.")));
- return -1;
- }
-
- /*
- * 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(LOG,
- (errmsg_internal("could not terminate unstartable process: error code %lu",
- GetLastError())));
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
- return -1;
- }
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
- ereport(LOG,
- (errmsg_internal("could not resume thread of unstarted process: error code %lu",
- GetLastError())));
- return -1;
- }
-
- /*
- * Queue a waiter to signal when this child dies. The wait will be handled
- * automatically by an operating system thread pool. The memory will be
- * freed by a later call to waitpid().
- */
- childinfo = palloc(sizeof(win32_deadchild_waitinfo));
- childinfo->procHandle = pi.hProcess;
- childinfo->procId = pi.dwProcessId;
-
- if (!RegisterWaitForSingleObject(&childinfo->waitHandle,
- pi.hProcess,
- pgwin32_deadchild_callback,
- childinfo,
- INFINITE,
- WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD))
- ereport(FATAL,
- (errmsg_internal("could not register process for wait: error code %lu",
- GetLastError())));
-
- /* Don't close pi.hProcess here - waitpid() needs access to it */
-
- 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
- * dispatch to the appropriate place.
- *
- * The first two command line arguments are expected to be "--forkFOO"
- * (where FOO indicates which postmaster child we are to become), and
- * the name of a variables file that we can read to load data that would
- * have been inherited by fork() on Unix. Remaining arguments go to the
- * subprocess FooMain() routine.
- */
-void
-SubPostmasterMain(int argc, char *argv[])
-{
- ClientSocket client_sock;
-
- /* In EXEC_BACKEND case we will not have inherited these settings */
- IsPostmasterEnvironment = true;
- whereToSendOutput = DestNone;
-
- /* Setup essential subsystems (to ensure elog() behaves sanely) */
- InitializeGUCOptions();
-
- /* Check we got appropriate args */
- if (argc < 3)
- elog(FATAL, "invalid subpostmaster invocation");
-
- /* Read in the variables file */
- memset(&client_sock, 0, sizeof(ClientSocket));
- read_backend_variables(argv[2], &client_sock);
-
- /* Close the postmaster's sockets (as soon as we know them) */
- ClosePostmasterPorts(strcmp(argv[1], "--forklog") == 0);
-
- /* Setup as postmaster child */
- InitPostmasterChild();
-
- /*
- * If appropriate, physically re-attach to shared memory segment. We want
- * to do this before going any further to ensure that we can attach at the
- * same address the postmaster used. On the other hand, if we choose not
- * to re-attach, we may have other cleanup to do.
- *
- * If testing EXEC_BACKEND on Linux, you should run this as root before
- * starting the postmaster:
- *
- * sysctl -w kernel.randomize_va_space=0
- *
- * This prevents using randomized stack and code addresses that cause the
- * child process's memory map to be different from the parent's, making it
- * 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], "--forkaux") == 0 ||
- strncmp(argv[1], "--forkbgworker", 14) == 0)
- PGSharedMemoryReAttach();
- else
- PGSharedMemoryNoReAttach();
-
- /* autovacuum needs this set before calling InitProcess */
- if (strcmp(argv[1], "--forkavlauncher") == 0)
- AutovacuumLauncherIAm();
- if (strcmp(argv[1], "--forkavworker") == 0)
- AutovacuumWorkerIAm();
-
- /* Read in remaining GUC variables */
- read_nondefault_variables();
-
- /*
- * Check that the data directory looks valid, which will also check the
- * privileges on the data directory and update our umask and file/group
- * variables for creating files later. Note: this should really be done
- * before we create any files or directories.
- */
- checkDataDir();
-
- /*
- * (re-)read control file, as it contains config. The postmaster will
- * already have read this, but this process doesn't know about that.
- */
- LocalProcessControlFile(false);
-
- /*
- * Reload any libraries that were preloaded by the postmaster. Since we
- * exec'd this process, those libraries didn't come along with us; but we
- * should load them into all child processes to be consistent with the
- * non-EXEC_BACKEND behavior.
- */
- process_shared_preload_libraries();
-
- /* Run backend or appropriate child */
- if (strcmp(argv[1], "--forkbackend") == 0)
- {
- CAC_state cac;
-
- Assert(argc == 4);
- cac = (CAC_state) atoi(argv[3]);
-
- /*
- * Need to reinitialize the SSL library in the backend, since the
- * context structures contain function pointers and cannot be passed
- * through the parameter file.
- *
- * If for some reason reload fails (maybe the user installed broken
- * key files), soldier on without SSL; that's better than all
- * connections becoming impossible.
- *
- * XXX should we do this in all child processes? For the moment it's
- * enough to do it in backend children.
- */
-#ifdef USE_SSL
- if (EnableSSL)
- {
- if (secure_initialize(false) == 0)
- LoadedSSL = true;
- else
- ereport(LOG,
- (errmsg("SSL configuration could not be loaded in child process")));
- }
-#endif
-
- /*
- * 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(&client_sock, cac);
-
- /* Restore basic shared memory pointers */
- InitShmemAccess(UsedShmemSegAddr);
-
- /* And run the backend */
- BackendRun(); /* does not return */
- }
- if (strcmp(argv[1], "--forkaux") == 0)
- {
- AuxProcType auxtype;
-
- Assert(argc == 4);
-
- /* Restore basic shared memory pointers */
- InitShmemAccess(UsedShmemSegAddr);
-
- auxtype = atoi(argv[3]);
- AuxiliaryProcessMain(auxtype); /* does not return */
- }
- if (strcmp(argv[1], "--forkavlauncher") == 0)
- {
- /* Restore basic shared memory pointers */
- InitShmemAccess(UsedShmemSegAddr);
-
- AutoVacLauncherMain(argc - 2, argv + 2); /* does not return */
- }
- if (strcmp(argv[1], "--forkavworker") == 0)
- {
- /* Restore basic shared memory pointers */
- InitShmemAccess(UsedShmemSegAddr);
-
- AutoVacWorkerMain(argc - 2, argv + 2); /* does not return */
- }
- if (strncmp(argv[1], "--forkbgworker", 14) == 0)
- {
- /* do this as early as possible; in particular, before InitProcess() */
- IsBackgroundWorker = true;
-
- /* Restore basic shared memory pointers */
- InitShmemAccess(UsedShmemSegAddr);
-
- StartBackgroundWorker();
- }
- if (strcmp(argv[1], "--forklog") == 0)
- {
- /* Do not want to attach to shared memory */
-
- SysLoggerMain(argc, argv); /* does not return */
- }
-
- abort(); /* shouldn't get here */
-}
-#endif /* EXEC_BACKEND */
-
-
-/*
- * ExitPostmaster -- cleanup
- *
- * Do NOT call exit() directly --- always go through here!
- */
-static void
-ExitPostmaster(int status)
-{
-#ifdef HAVE_PTHREAD_IS_THREADED_NP
-
- /*
- * There is no known cause for a postmaster to become multithreaded after
- * startup. Recheck to account for the possibility of unknown causes.
- * This message uses LOG level, because an unclean shutdown at this point
- * would usually not look much different from a clean shutdown.
- */
- if (pthread_is_threaded_np() != 0)
- ereport(LOG,
- (errcode(ERRCODE_INTERNAL_ERROR),
- errmsg_internal("postmaster became multithreaded"),
- errdetail("Please report this to <%s>.", PACKAGE_BUGREPORT)));
-#endif
-
- /* should cleanup shared memory and kill all backends */
-
- /*
- * Not sure of the semantics here. When the Postmaster dies, should the
- * backends all be killed? probably not.
- *
- * MUST -- vadim 05-10-1999
- */
-
- proc_exit(status);
-}
-
-/*
- * Handle pmsignal conditions representing requests from backends,
- * and check for promote and logrotate requests from pg_ctl.
- */
-static void
-process_pm_pmsignal(void)
-{
- pending_pm_pmsignal = false;
-
- ereport(DEBUG2,
- (errmsg_internal("postmaster received pmsignal signal")));
-
- /*
- * RECOVERY_STARTED and BEGIN_HOT_STANDBY signals are ignored in
- * unexpected states. If the startup process quickly starts up, completes
- * recovery, exits, we might process the death of the startup process
- * first. We don't want to go back to recovery in that case.
- */
- if (CheckPostmasterSignal(PMSIGNAL_RECOVERY_STARTED) &&
- pmState == PM_STARTUP && Shutdown == NoShutdown)
- {
- /* WAL redo has started. We're out of reinitialization. */
- FatalError = false;
- AbortStartTime = 0;
-
- /*
- * Start the archiver if we're responsible for (re-)archiving received
- * files.
- */
- Assert(PgArchPID == 0);
- if (XLogArchivingAlways())
- PgArchPID = StartArchiver();
-
- /*
- * If we aren't planning to enter hot standby mode later, treat
- * RECOVERY_STARTED as meaning we're out of startup, and report status
- * accordingly.
- */
- if (!EnableHotStandby)
- {
- AddToDataDirLockFile(LOCK_FILE_LINE_PM_STATUS, PM_STATUS_STANDBY);
-#ifdef USE_SYSTEMD
- sd_notify(0, "READY=1");
-#endif
- }
-
- pmState = PM_RECOVERY;
- }
-
- if (CheckPostmasterSignal(PMSIGNAL_BEGIN_HOT_STANDBY) &&
- pmState == PM_RECOVERY && Shutdown == NoShutdown)
- {
- ereport(LOG,
- (errmsg("database system is ready to accept read-only connections")));
-
- /* Report status */
- AddToDataDirLockFile(LOCK_FILE_LINE_PM_STATUS, PM_STATUS_READY);
-#ifdef USE_SYSTEMD
- sd_notify(0, "READY=1");
-#endif
-
- pmState = PM_HOT_STANDBY;
- connsAllowed = true;
-
- /* Some workers may be scheduled to start now */
- StartWorkerNeeded = true;
- }
-
- /* Process background worker state changes. */
- if (CheckPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE))
- {
- /* Accept new worker requests only if not stopping. */
- BackgroundWorkerStateChange(pmState < PM_STOP_BACKENDS);
- StartWorkerNeeded = true;
- }
-
- if (StartWorkerNeeded || HaveCrashedWorker)
- maybe_start_bgworkers();
-
- /* Tell syslogger to rotate logfile if requested */
- if (SysLoggerPID != 0)
- {
- if (CheckLogrotateSignal())
- {
- signal_child(SysLoggerPID, SIGUSR1);
- RemoveLogrotateSignalFiles();
- }
- else if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE))
- {
- signal_child(SysLoggerPID, SIGUSR1);
- }
- }
-
- if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER) &&
- Shutdown <= SmartShutdown && pmState < PM_STOP_BACKENDS)
- {
- /*
- * Start one iteration of the autovacuum daemon, even if autovacuuming
- * is nominally not enabled. This is so we can have an active defense
- * against transaction ID wraparound. We set a flag for the main loop
- * to do it rather than trying to do it here --- this is because the
- * autovac process itself may send the signal, and we want to handle
- * that by launching another iteration as soon as the current one
- * completes.
- */
- start_autovac_launcher = true;
- }
-
- if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER) &&
- Shutdown <= SmartShutdown && pmState < PM_STOP_BACKENDS)
- {
- /* The autovacuum launcher wants us to start a worker process. */
- StartAutovacuumWorker();
- }
-
- if (CheckPostmasterSignal(PMSIGNAL_START_WALRECEIVER))
- {
- /* Startup Process wants us to start the walreceiver process. */
- /* Start immediately if possible, else remember request for later. */
- WalReceiverRequested = true;
- MaybeStartWalReceiver();
+ /* Startup Process wants us to start the walreceiver process. */
+ /* Start immediately if possible, else remember request for later. */
+ WalReceiverRequested = true;
+ MaybeStartWalReceiver();
}
/*
@@ -5276,93 +4664,23 @@ CountChildren(int target)
* to start subprocess.
*/
static pid_t
-StartChildProcess(AuxProcType type)
+StartChildProcess(PostmasterChildType type)
{
pid_t pid;
-#ifdef EXEC_BACKEND
- {
- char *av[10];
- int ac = 0;
- char typebuf[32];
-
- /*
- * Set up command-line arguments for subprocess
- */
- av[ac++] = "postgres";
- av[ac++] = "--forkaux";
- av[ac++] = NULL; /* filled in by postmaster_forkexec */
-
- snprintf(typebuf, sizeof(typebuf), "%d", type);
- av[ac++] = typebuf;
-
- av[ac] = NULL;
- Assert(ac < lengthof(av));
-
- pid = postmaster_forkexec(ac, av);
- }
-#else /* !EXEC_BACKEND */
- pid = fork_process();
-
- if (pid == 0) /* child */
- {
- InitPostmasterChild();
-
- /* Close the postmaster's sockets */
- ClosePostmasterPorts(false);
-
- /* Release postmaster's working memory context */
- MemoryContextSwitchTo(TopMemoryContext);
- MemoryContextDelete(PostmasterContext);
- PostmasterContext = NULL;
-
- AuxiliaryProcessMain(type); /* does not return */
- }
-#endif /* EXEC_BACKEND */
-
+ pid = postmaster_child_launch(type, NULL, 0, NULL);
if (pid < 0)
{
/* in parent, fork failed */
- int save_errno = errno;
-
- errno = save_errno;
- switch (type)
- {
- case StartupProcess:
- ereport(LOG,
- (errmsg("could not fork startup process: %m")));
- break;
- case ArchiverProcess:
- ereport(LOG,
- (errmsg("could not fork archiver 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;
- }
+ /* XXX: translation? */
+ ereport(LOG,
+ (errmsg("could not fork %s process: %m", PostmasterChildName(type))));
/*
* fork failure is fatal during startup, but there's no need to choke
* immediately if starting other child types fails.
*/
- if (type == StartupProcess)
+ if (type == PMC_STARTUP)
ExitPostmaster(1);
return 0;
}
@@ -5612,32 +4930,6 @@ BackgroundWorkerUnblockSignals(void)
sigprocmask(SIG_SETMASK, &UnBlockSig, NULL);
}
-#ifdef EXEC_BACKEND
-static pid_t
-bgworker_forkexec(BackgroundWorker *worker)
-{
- char *av[10];
- int ac = 0;
- char forkav[MAXPGPATH];
- pid_t result;
-
- snprintf(forkav, MAXPGPATH, "--forkbgworker");
-
- av[ac++] = "postgres";
- av[ac++] = forkav;
- av[ac++] = NULL; /* filled in by postmaster_forkexec */
- av[ac] = NULL;
-
- Assert(ac < lengthof(av));
-
- MyBgworkerEntry = worker;
- result = postmaster_forkexec(ac, av);
- MyBgworkerEntry = NULL;
-
- return result;
-}
-#endif
-
/*
* Start a new bgworker.
* Starting time conditions must have been checked already.
@@ -5674,65 +4966,32 @@ do_start_bgworker(RegisteredBgWorker *rw)
(errmsg_internal("starting background worker process \"%s\"",
rw->rw_worker.bgw_name)));
-#ifdef EXEC_BACKEND
- switch ((worker_pid = bgworker_forkexec(&rw->rw_worker)))
-#else
- switch ((worker_pid = fork_process()))
-#endif
+ worker_pid = postmaster_child_launch(PMC_BGWORKER, (char *) &rw->rw_worker, sizeof(BackgroundWorker), NULL);
+ if (worker_pid == -1)
{
- 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;
- pfree(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();
+ /* 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;
+ pfree(rw->rw_backend);
+ rw->rw_backend = NULL;
+ /* mark entry as crashed, so we'll try again later */
+ rw->rw_crashed_at = GetCurrentTimestamp();
+ return false;
+ }
- 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);
+ /* 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);
+ ShmemBackendArrayAdd(rw->rw_backend);
#endif
- return true;
- }
-
- return false;
+ return true;
}
/*
@@ -5984,351 +5243,6 @@ PostmasterMarkPIDForWorkerNotify(int pid)
#ifdef EXEC_BACKEND
-/*
- * The following need to be available to the save/restore_backend_variables
- * functions. They are marked NON_EXEC_STATIC in their home modules.
- */
-extern slock_t *ShmemLock;
-extern slock_t *ProcStructLock;
-extern PGPROC *AuxiliaryProcs;
-extern PMSignalData *PMSignalState;
-extern pg_time_t first_syslogger_file_time;
-
-#ifndef WIN32
-#define write_inheritable_socket(dest, src, childpid) ((*(dest) = (src)), true)
-#define read_inheritable_socket(dest, src) (*(dest) = *(src))
-#else
-static bool write_duplicated_handle(HANDLE *dest, HANDLE src, HANDLE child);
-static bool write_inheritable_socket(InheritableSocket *dest, SOCKET src,
- pid_t childPid);
-static void read_inheritable_socket(SOCKET *dest, InheritableSocket *src);
-#endif
-
-
-/* Save critical backend variables into the BackendParameters struct */
-#ifndef WIN32
-static bool
-save_backend_variables(BackendParameters *param, ClientSocket *client_sock)
-#else
-static bool
-save_backend_variables(BackendParameters *param, ClientSocket *client_sock,
- HANDLE childProcess, pid_t childPid)
-#endif
-{
- memcpy(¶m->client_sock, client_sock, sizeof(ClientSocket));
- if (!write_inheritable_socket(¶m->serialized_sock, client_sock->sock, childPid))
- return false;
-
- strlcpy(param->DataDir, DataDir, MAXPGPATH);
-
- param->MyCancelKey = MyCancelKey;
- param->MyPMChildSlot = MyPMChildSlot;
-
-#ifdef WIN32
- param->ShmemProtectiveRegion = ShmemProtectiveRegion;
-#endif
- param->UsedShmemSegID = UsedShmemSegID;
- param->UsedShmemSegAddr = UsedShmemSegAddr;
-
- param->ShmemLock = ShmemLock;
- param->ShmemVariableCache = ShmemVariableCache;
- param->ShmemBackendArray = ShmemBackendArray;
-
-#ifndef HAVE_SPINLOCKS
- param->SpinlockSemaArray = SpinlockSemaArray;
-#endif
- param->NamedLWLockTrancheRequests = NamedLWLockTrancheRequests;
- param->NamedLWLockTrancheArray = NamedLWLockTrancheArray;
- param->MainLWLockArray = MainLWLockArray;
- param->ProcStructLock = ProcStructLock;
- param->ProcGlobal = ProcGlobal;
- param->AuxiliaryProcs = AuxiliaryProcs;
- param->PreparedXactProcs = PreparedXactProcs;
- param->PMSignalState = PMSignalState;
-
- param->PostmasterPid = PostmasterPid;
- param->PgStartTime = PgStartTime;
- param->PgReloadTime = PgReloadTime;
- param->first_syslogger_file_time = first_syslogger_file_time;
-
- param->redirection_done = redirection_done;
- param->IsBinaryUpgrade = IsBinaryUpgrade;
- param->query_id_enabled = query_id_enabled;
- param->max_safe_fds = max_safe_fds;
-
- param->MaxBackends = MaxBackends;
-
-#ifdef WIN32
- param->PostmasterHandle = PostmasterHandle;
- if (!write_duplicated_handle(¶m->initial_signal_pipe,
- pgwin32_create_signal_listener(childPid),
- childProcess))
- return false;
-#else
- memcpy(¶m->postmaster_alive_fds, &postmaster_alive_fds,
- sizeof(postmaster_alive_fds));
-#endif
-
- memcpy(¶m->syslogPipe, &syslogPipe, sizeof(syslogPipe));
-
- strlcpy(param->my_exec_path, my_exec_path, MAXPGPATH);
-
- strlcpy(param->pkglib_path, pkglib_path, MAXPGPATH);
-
- if (MyBgworkerEntry)
- memcpy(¶m->MyBgworkerEntry, MyBgworkerEntry, sizeof(BackgroundWorker));
- else
- memset(¶m->MyBgworkerEntry, 0, sizeof(BackgroundWorker));
-
- return true;
-}
-
-
-#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 bool
-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(LOG,
- (errmsg_internal("could not duplicate handle to be written to backend parameter file: error code %lu",
- GetLastError())));
- return false;
- }
-
- *dest = hChild;
- return true;
-}
-
-/*
- * 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 bool
-write_inheritable_socket(InheritableSocket *dest, SOCKET src, pid_t childpid)
-{
- dest->origsocket = src;
- if (src != 0 && src != PGINVALID_SOCKET)
- {
- /* Actual socket */
- if (WSADuplicateSocket(src, childpid, &dest->wsainfo) != 0)
- {
- ereport(LOG,
- (errmsg("could not duplicate socket %d for use in backend: error code %d",
- (int) src, WSAGetLastError())));
- return false;
- }
- }
- return true;
-}
-
-/*
- * Read a duplicate socket structure back, and get the socket descriptor.
- */
-static void
-read_inheritable_socket(SOCKET *dest, InheritableSocket *src)
-{
- SOCKET s;
-
- if (src->origsocket == PGINVALID_SOCKET || src->origsocket == 0)
- {
- /* 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;
-
- /*
- * 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 *id, ClientSocket *client_sock)
-{
- BackendParameters param;
-
-#ifndef WIN32
- /* Non-win32 implementation reads from file */
- FILE *fp;
-
- /* Open file */
- fp = AllocateFile(id, PG_BINARY_R);
- if (!fp)
- {
- write_stderr("could not open backend variables file \"%s\": %s\n",
- id, strerror(errno));
- exit(1);
- }
-
- if (fread(¶m, 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);
- }
-#else
- /* Win32 version uses mapped file */
- HANDLE paramHandle;
- BackendParameters *paramp;
-
-#ifdef _WIN64
- paramHandle = (HANDLE) _atoi64(id);
-#else
- paramHandle = (HANDLE) atol(id);
-#endif
- paramp = MapViewOfFile(paramHandle, FILE_MAP_READ, 0, 0, 0);
- if (!paramp)
- {
- write_stderr("could not map view of backend variables: error code %lu\n",
- GetLastError());
- exit(1);
- }
-
- memcpy(¶m, paramp, sizeof(BackendParameters));
-
- if (!UnmapViewOfFile(paramp))
- {
- write_stderr("could not unmap view of backend variables: error code %lu\n",
- GetLastError());
- exit(1);
- }
-
- if (!CloseHandle(paramHandle))
- {
- write_stderr("could not close handle to backend parameter variables: error code %lu\n",
- GetLastError());
- exit(1);
- }
-#endif
-
- restore_backend_variables(¶m, client_sock);
-}
-
-/* Restore critical backend variables from the BackendParameters struct */
-static void
-restore_backend_variables(BackendParameters *param, ClientSocket *client_sock)
-{
- memcpy(client_sock, ¶m->client_sock, sizeof(ClientSocket));
- read_inheritable_socket(&client_sock->sock, ¶m->serialized_sock);
-
- SetDataDir(param->DataDir);
-
- MyCancelKey = param->MyCancelKey;
- MyPMChildSlot = param->MyPMChildSlot;
-
-#ifdef WIN32
- ShmemProtectiveRegion = param->ShmemProtectiveRegion;
-#endif
- UsedShmemSegID = param->UsedShmemSegID;
- UsedShmemSegAddr = param->UsedShmemSegAddr;
-
- ShmemLock = param->ShmemLock;
- ShmemVariableCache = param->ShmemVariableCache;
- ShmemBackendArray = param->ShmemBackendArray;
-
-#ifndef HAVE_SPINLOCKS
- SpinlockSemaArray = param->SpinlockSemaArray;
-#endif
- NamedLWLockTrancheRequests = param->NamedLWLockTrancheRequests;
- NamedLWLockTrancheArray = param->NamedLWLockTrancheArray;
- MainLWLockArray = param->MainLWLockArray;
- ProcStructLock = param->ProcStructLock;
- ProcGlobal = param->ProcGlobal;
- AuxiliaryProcs = param->AuxiliaryProcs;
- PreparedXactProcs = param->PreparedXactProcs;
- PMSignalState = param->PMSignalState;
-
- PostmasterPid = param->PostmasterPid;
- PgStartTime = param->PgStartTime;
- PgReloadTime = param->PgReloadTime;
- first_syslogger_file_time = param->first_syslogger_file_time;
-
- redirection_done = param->redirection_done;
- IsBinaryUpgrade = param->IsBinaryUpgrade;
- query_id_enabled = param->query_id_enabled;
- max_safe_fds = param->max_safe_fds;
-
- MaxBackends = param->MaxBackends;
-
-#ifdef WIN32
- PostmasterHandle = param->PostmasterHandle;
- pgwin32_initial_signal_pipe = param->initial_signal_pipe;
-#else
- memcpy(&postmaster_alive_fds, ¶m->postmaster_alive_fds,
- sizeof(postmaster_alive_fds));
-#endif
-
- memcpy(&syslogPipe, ¶m->syslogPipe, sizeof(syslogPipe));
-
- strlcpy(my_exec_path, param->my_exec_path, MAXPGPATH);
-
- strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
-
- if (param->MyBgworkerEntry.bgw_name[0] != '\0')
- {
- MyBgworkerEntry = (BackgroundWorker *)
- MemoryContextAlloc(TopMemoryContext, sizeof(BackgroundWorker));
- memcpy(MyBgworkerEntry, ¶m->MyBgworkerEntry, sizeof(BackgroundWorker));
- }
-
- /*
- * We need to restore fd.c's counts of externally-opened FDs; to avoid
- * confusion, be sure to do this after restoring max_safe_fds. (Note:
- * BackendInitialize will handle this for port->sock.)
- */
-#ifndef WIN32
- if (postmaster_alive_fds[0] >= 0)
- ReserveExternalFD();
- if (postmaster_alive_fds[1] >= 0)
- ReserveExternalFD();
-#endif
-}
-
-
Size
ShmemBackendArraySize(void)
{
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index efc2580536a..0db0843ab8e 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -25,6 +25,7 @@
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/auxprocess.h"
#include "postmaster/interrupt.h"
#include "postmaster/startup.h"
#include "storage/ipc.h"
@@ -227,8 +228,14 @@ StartupProcExit(int code, Datum arg)
* ----------------------------------
*/
void
-StartupProcessMain(void)
+StartupProcessMain(char *startup_data, size_t startup_data_len)
{
+ Assert(startup_data_len == 0);
+
+ MyAuxProcType = StartupProcess;
+ MyBackendType = B_STARTUP;
+ AuxiliaryProcessInit();
+
/* Arrange to clean up at startup process exit */
on_shmem_exit(StartupProcExit, 0);
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index 858a2f6b2b9..6b06311c9d5 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -39,7 +39,6 @@
#include "pgstat.h"
#include "pgtime.h"
#include "port/pg_bitutils.h"
-#include "postmaster/fork_process.h"
#include "postmaster/interrupt.h"
#include "postmaster/postmaster.h"
#include "postmaster/syslogger.h"
@@ -50,6 +49,7 @@
#include "storage/pg_shmem.h"
#include "tcop/tcopprot.h"
#include "utils/guc.h"
+#include "utils/memutils.h"
#include "utils/ps_status.h"
#include "utils/timestamp.h"
@@ -134,10 +134,7 @@ static volatile sig_atomic_t rotation_requested = false;
#ifdef EXEC_BACKEND
static int syslogger_fdget(FILE *file);
static FILE *syslogger_fdopen(int fd);
-static pid_t syslogger_forkexec(void);
-static void syslogger_parseArgs(int argc, char *argv[]);
#endif
-NON_EXEC_STATIC void SysLoggerMain(int argc, char *argv[]) pg_attribute_noreturn();
static void process_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
static void flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
static FILE *logfile_open(const char *filename, const char *mode,
@@ -156,13 +153,18 @@ static void set_next_rotation_time(void);
static void sigUsr1Handler(SIGNAL_ARGS);
static void update_metainfo_datafile(void);
+typedef struct {
+ int syslogFile;
+ int csvlogFile;
+ int jsonlogFile;
+} syslogger_startup_data;
/*
* Main entry point for syslogger process
* argc/argv parameters are valid only in EXEC_BACKEND case.
*/
-NON_EXEC_STATIC void
-SysLoggerMain(int argc, char *argv[])
+void
+SysLoggerMain(char *startup_data, size_t startup_data_len)
{
#ifndef WIN32
char logbuffer[READ_BUF_SIZE];
@@ -174,11 +176,35 @@ SysLoggerMain(int argc, char *argv[])
pg_time_t now;
WaitEventSet *wes;
- now = MyStartTime;
+ /* Release postmaster's working memory context */
+ if (PostmasterContext)
+ {
+ MemoryContextDelete(PostmasterContext);
+ PostmasterContext = NULL;
+ }
+ /*
+ * Re-open the error output files that were opened by SysLogger_Start().
+ *
+ * We expect this will always succeed, which is too optimistic, but if it
+ * fails there's not a lot we can do to report the problem anyway. As
+ * coded, we'll just crash on a null pointer dereference after failure...
+ */
#ifdef EXEC_BACKEND
- syslogger_parseArgs(argc, argv);
-#endif /* EXEC_BACKEND */
+ {
+ syslogger_startup_data *info;
+
+ Assert(startup_data_len == sizeof(info));
+ info = (syslogger_startup_data *) startup_data;
+ syslogFile = syslogger_fdopen(info->syslogFile);
+ csvlogFile = syslogger_fdopen(info->csvlogFile);
+ jsonlogFile = syslogger_fdopen(info->jsonlogFile);
+ }
+#else
+ Assert(startup_data_len == 0);
+#endif
+
+ now = MyStartTime;
MyBackendType = B_LOGGER;
init_ps_display(NULL);
@@ -568,6 +594,9 @@ SysLogger_Start(void)
{
pid_t sysloggerPid;
char *filename;
+#ifdef EXEC_BACKEND
+ syslogger_startup_data startup_data;
+#endif /* EXEC_BACKEND */
if (!Logging_collector)
return 0;
@@ -667,112 +696,95 @@ SysLogger_Start(void)
}
#ifdef EXEC_BACKEND
- switch ((sysloggerPid = syslogger_forkexec()))
+ startup_data.syslogFile = syslogger_fdget(syslogFile);
+ startup_data.csvlogFile = syslogger_fdget(csvlogFile);
+ startup_data.jsonlogFile = syslogger_fdget(jsonlogFile);
+ sysloggerPid = postmaster_child_launch(PMC_SYSLOGGER, (char *) &startup_data, sizeof(startup_data), NULL);
#else
- switch ((sysloggerPid = fork_process()))
-#endif
- {
- case -1:
- ereport(LOG,
- (errmsg("could not fork system logger: %m")));
- return 0;
-
-#ifndef EXEC_BACKEND
- case 0:
- /* in postmaster child ... */
- InitPostmasterChild();
-
- /* Close the postmaster's sockets */
- ClosePostmasterPorts(true);
-
- /* Drop our connection to postmaster's shared memory, as well */
- dsm_detach_all();
- PGSharedMemoryDetach();
+ sysloggerPid = postmaster_child_launch(PMC_SYSLOGGER, NULL, 0, NULL);
+#endif /* EXEC_BACKEND */
- /* do the work */
- SysLoggerMain(0, NULL);
- break;
-#endif
+ if (sysloggerPid == -1)
+ {
+ ereport(LOG,
+ (errmsg("could not fork system logger: %m")));
+ return 0;
+ }
- default:
- /* success, in postmaster */
+ /* success, in postmaster */
- /* now we redirect stderr, if not done already */
- if (!redirection_done)
- {
+ /* now we redirect stderr, if not done already */
+ if (!redirection_done)
+ {
#ifdef WIN32
- int fd;
+ int fd;
#endif
- /*
- * Leave a breadcrumb trail when redirecting, in case the user
- * forgets that redirection is active and looks only at the
- * original stderr target file.
- */
- ereport(LOG,
- (errmsg("redirecting log output to logging collector process"),
- errhint("Future log output will appear in directory \"%s\".",
- Log_directory)));
+ /*
+ * Leave a breadcrumb trail when redirecting, in case the user
+ * forgets that redirection is active and looks only at the
+ * original stderr target file.
+ */
+ ereport(LOG,
+ (errmsg("redirecting log output to logging collector process"),
+ errhint("Future log output will appear in directory \"%s\".",
+ Log_directory)));
#ifndef WIN32
- fflush(stdout);
- if (dup2(syslogPipe[1], STDOUT_FILENO) < 0)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not redirect stdout: %m")));
- fflush(stderr);
- if (dup2(syslogPipe[1], STDERR_FILENO) < 0)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not redirect stderr: %m")));
- /* Now we are done with the write end of the pipe. */
- close(syslogPipe[1]);
- syslogPipe[1] = -1;
+ fflush(stdout);
+ if (dup2(syslogPipe[1], STDOUT_FILENO) < 0)
+ ereport(FATAL,
+ (errcode_for_file_access(),
+ errmsg("could not redirect stdout: %m")));
+ fflush(stderr);
+ if (dup2(syslogPipe[1], STDERR_FILENO) < 0)
+ ereport(FATAL,
+ (errcode_for_file_access(),
+ errmsg("could not redirect stderr: %m")));
+ /* Now we are done with the write end of the pipe. */
+ close(syslogPipe[1]);
+ syslogPipe[1] = -1;
#else
- /*
- * open the pipe in binary mode and make sure stderr is binary
- * after it's been dup'ed into, to avoid disturbing the pipe
- * chunking protocol.
- */
- fflush(stderr);
- fd = _open_osfhandle((intptr_t) syslogPipe[1],
- _O_APPEND | _O_BINARY);
- if (dup2(fd, STDERR_FILENO) < 0)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not redirect stderr: %m")));
- close(fd);
- _setmode(STDERR_FILENO, _O_BINARY);
+ /*
+ * open the pipe in binary mode and make sure stderr is binary
+ * after it's been dup'ed into, to avoid disturbing the pipe
+ * chunking protocol.
+ */
+ fflush(stderr);
+ fd = _open_osfhandle((intptr_t) syslogPipe[1],
+ _O_APPEND | _O_BINARY);
+ if (dup2(fd, STDERR_FILENO) < 0)
+ ereport(FATAL,
+ (errcode_for_file_access(),
+ errmsg("could not redirect stderr: %m")));
+ close(fd);
+ _setmode(STDERR_FILENO, _O_BINARY);
- /*
- * Now we are done with the write end of the pipe.
- * CloseHandle() must not be called because the preceding
- * close() closes the underlying handle.
- */
- syslogPipe[1] = 0;
+ /*
+ * Now we are done with the write end of the pipe.
+ * CloseHandle() must not be called because the preceding
+ * close() closes the underlying handle.
+ */
+ syslogPipe[1] = 0;
#endif
- redirection_done = true;
- }
-
- /* postmaster will never write the file(s); close 'em */
- fclose(syslogFile);
- syslogFile = NULL;
- if (csvlogFile != NULL)
- {
- fclose(csvlogFile);
- csvlogFile = NULL;
- }
- if (jsonlogFile != NULL)
- {
- fclose(jsonlogFile);
- jsonlogFile = NULL;
- }
- return (int) sysloggerPid;
+ redirection_done = true;
}
- /* we should never reach here */
- return 0;
+ /* postmaster will never write the file(s); close 'em */
+ fclose(syslogFile);
+ syslogFile = NULL;
+ if (csvlogFile != NULL)
+ {
+ fclose(csvlogFile);
+ csvlogFile = NULL;
+ }
+ if (jsonlogFile != NULL)
+ {
+ fclose(jsonlogFile);
+ jsonlogFile = NULL;
+ }
+ return (int) sysloggerPid;
}
@@ -831,69 +843,6 @@ syslogger_fdopen(int fd)
return file;
}
-
-/*
- * syslogger_forkexec() -
- *
- * Format up the arglist for, then fork and exec, a syslogger process
- */
-static pid_t
-syslogger_forkexec(void)
-{
- char *av[10];
- int ac = 0;
- char filenobuf[32];
- char csvfilenobuf[32];
- char jsonfilenobuf[32];
-
- av[ac++] = "postgres";
- av[ac++] = "--forklog";
- av[ac++] = NULL; /* filled in by postmaster_forkexec */
-
- /* static variables (those not passed by write_backend_variables) */
- snprintf(filenobuf, sizeof(filenobuf), "%d",
- syslogger_fdget(syslogFile));
- av[ac++] = filenobuf;
- snprintf(csvfilenobuf, sizeof(csvfilenobuf), "%d",
- syslogger_fdget(csvlogFile));
- av[ac++] = csvfilenobuf;
- snprintf(jsonfilenobuf, sizeof(jsonfilenobuf), "%d",
- syslogger_fdget(jsonlogFile));
- av[ac++] = jsonfilenobuf;
-
- av[ac] = NULL;
- Assert(ac < lengthof(av));
-
- return postmaster_forkexec(ac, av);
-}
-
-/*
- * syslogger_parseArgs() -
- *
- * Extract data from the arglist for exec'ed syslogger process
- */
-static void
-syslogger_parseArgs(int argc, char *argv[])
-{
- int fd;
-
- Assert(argc == 6);
- argv += 3;
-
- /*
- * Re-open the error output files that were opened by SysLogger_Start().
- *
- * We expect this will always succeed, which is too optimistic, but if it
- * fails there's not a lot we can do to report the problem anyway. As
- * coded, we'll just crash on a null pointer dereference after failure...
- */
- fd = atoi(*argv++);
- syslogFile = syslogger_fdopen(fd);
- fd = atoi(*argv++);
- csvlogFile = syslogger_fdopen(fd);
- fd = atoi(*argv++);
- jsonlogFile = syslogger_fdopen(fd);
-}
#endif /* EXEC_BACKEND */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 266fbc23399..886c8bd8f6a 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -48,6 +48,7 @@
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/auxprocess.h"
#include "postmaster/interrupt.h"
#include "postmaster/walwriter.h"
#include "storage/bufmgr.h"
@@ -88,13 +89,19 @@ static void HandleWalWriterInterrupts(void);
* basic execution environment, but not enabled signals yet.
*/
void
-WalWriterMain(void)
+WalWriterMain(char *startup_data, size_t startup_data_len)
{
sigjmp_buf local_sigjmp_buf;
MemoryContext walwriter_context;
int left_till_hibernate;
bool hibernating;
+ Assert(startup_data_len == 0);
+
+ MyAuxProcType = WalWriterProcess;
+ MyBackendType = B_WAL_WRITER;
+ AuxiliaryProcessInit();
+
/*
* Properly accept or ignore signals the postmaster might send us
*
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 87b5593d2db..91f037f070b 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -30,7 +30,6 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/bgworker.h"
-#include "postmaster/fork_process.h"
#include "postmaster/interrupt.h"
#include "postmaster/postmaster.h"
#include "replication/logicallauncher.h"
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index feff7094351..01e8c3fd937 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -65,6 +65,7 @@
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/auxprocess.h"
#include "postmaster/interrupt.h"
#include "replication/walreceiver.h"
#include "replication/walsender.h"
@@ -184,7 +185,7 @@ ProcessWalRcvInterrupts(void)
/* Main entry point for walreceiver process */
void
-WalReceiverMain(void)
+WalReceiverMain(char *startup_data, size_t startup_data_len)
{
char conninfo[MAXCONNINFO];
char *tmp_conninfo;
@@ -200,10 +201,17 @@ WalReceiverMain(void)
char *sender_host = NULL;
int sender_port = 0;
+ Assert(startup_data_len == 0);
+
+ MyAuxProcType = WalReceiverProcess;
+ MyBackendType = B_WAL_RECEIVER;
+ AuxiliaryProcessInit();
+
/*
* WalRcv should be set up already (if we are a backend, we inherit this
* by fork() or EXEC_BACKEND mechanism from the postmaster).
*/
+ // FIXME: could this be passed in startup_data instead? Would it be better?
Assert(walrcv != NULL);
/*
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 5465fa19646..d65d461340c 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -144,6 +144,8 @@ InitShmemAllocation(void)
/*
* Initialize ShmemVariableCache for transaction manager. (This doesn't
* really belong here, but not worth moving.)
+ *
+ * XXX: we really should move this
*/
ShmemVariableCache = (VariableCache)
ShmemAlloc(sizeof(*ShmemVariableCache));
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 01b6cc1f7d3..6bc5a47864e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4204,6 +4204,7 @@ PostgresMain(const char *dbname, const char *username)
* *MyProcPort, because ConnCreate() allocated that space with malloc()
* ... else we'd need to copy the Port data first. Also, subsidiary data
* such as the username isn't lost either; see ProcessStartupPacket().
+ * XXX
*/
if (PostmasterContext)
{
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 011ec18015a..e7dde1b4e1f 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -44,6 +44,7 @@ volatile uint32 CritSectionCount = 0;
int MyProcPid;
pg_time_t MyStartTime;
TimestampTz MyStartTimestamp;
+struct ClientSocket *MyClientSocket;
struct Port *MyProcPort;
int32 MyCancelKey;
int MyPMChildSlot;
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index a604432126c..fb0f26d1814 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -85,139 +85,6 @@ bool IgnoreSystemIndexes = false;
* ----------------------------------------------------------------
*/
-/*
- * Initialize the basic environment for a postmaster child
- *
- * Should be called as early as possible after the child's startup. However,
- * on EXEC_BACKEND builds it does need to be after read_backend_variables().
- */
-void
-InitPostmasterChild(void)
-{
- IsUnderPostmaster = true; /* we are a postmaster subprocess now */
-
- /*
- * 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
-
- /*
- * Set reference point for stack-depth checking. This might seem
- * redundant in !EXEC_BACKEND builds; but it's not because the postmaster
- * launches its children from signal handlers, so we might be running on
- * an alternative stack.
- */
- (void) set_stack_base();
-
- InitProcessGlobals();
-
- /*
- * make sure stderr is in binary mode before anything can possibly be
- * written to it, in case it's actually the syslogger pipe, so the pipe
- * chunking protocol isn't disturbed. Non-logpipe data gets translated on
- * redirection (e.g. via pg_ctl -l) anyway.
- */
-#ifdef WIN32
- _setmode(fileno(stderr), _O_BINARY);
-#endif
-
- /* We don't want the postmaster's proc_exit() handlers */
- on_exit_reset();
-
- /* In EXEC_BACKEND case we will not have inherited BlockSig etc values */
-#ifdef EXEC_BACKEND
- pqinitmask();
-#endif
-
- /* Initialize process-local latch support */
- InitializeLatchSupport();
- InitProcessLocalLatch();
- InitializeLatchWaitSet();
-
- /*
- * If possible, make this process a group leader, so that the postmaster
- * can signal any child processes too. Not all processes will have
- * children, but for consistency we make all postmaster child processes do
- * this.
- */
-#ifdef HAVE_SETSID
- if (setsid() < 0)
- elog(FATAL, "setsid() failed: %m");
-#endif
-
- /*
- * Every postmaster child process is expected to respond promptly to
- * SIGQUIT at all times. Therefore we centrally remove SIGQUIT from
- * BlockSig and install a suitable signal handler. (Client-facing
- * processes may choose to replace this default choice of handler with
- * quickdie().) All other blockable signals remain blocked for now.
- */
- pqsignal(SIGQUIT, SignalHandlerForCrashExit);
-
- sigdelset(&BlockSig, SIGQUIT);
- sigprocmask(SIG_SETMASK, &BlockSig, NULL);
-
- /* Request a signal if the postmaster dies, if possible. */
- PostmasterDeathSignalInit();
-
- /* Don't give the pipe to subprograms that we execute. */
-#ifndef WIN32
- if (fcntl(postmaster_alive_fds[POSTMASTER_FD_WATCH], F_SETFD, FD_CLOEXEC) < 0)
- ereport(FATAL,
- (errcode_for_socket_access(),
- errmsg_internal("could not set postmaster death monitoring pipe to FD_CLOEXEC mode: %m")));
-#endif
-}
-
-/*
- * Initialize the basic environment for a standalone process.
- *
- * argv0 has to be suitable to find the program's executable.
- */
-void
-InitStandaloneProcess(const char *argv0)
-{
- Assert(!IsPostmasterEnvironment);
-
- MyBackendType = B_STANDALONE_BACKEND;
-
- /*
- * Start our win32 signal implementation
- */
-#ifdef WIN32
- pgwin32_signal_initialize();
-#endif
-
- InitProcessGlobals();
-
- /* Initialize process-local latch support */
- InitializeLatchSupport();
- InitProcessLocalLatch();
- InitializeLatchWaitSet();
-
- /*
- * For consistency with InitPostmasterChild, initialize signal mask here.
- * But we don't unblock SIGQUIT or provide a default handler for it.
- */
- pqinitmask();
- sigprocmask(SIG_SETMASK, &BlockSig, NULL);
-
- /* Compute paths, no postmaster to inherit from */
- if (my_exec_path[0] == '\0')
- {
- if (find_my_exec(argv0, my_exec_path) < 0)
- elog(FATAL, "%s: could not locate my own executable path",
- argv0);
- }
-
- if (pkglib_path[0] == '\0')
- get_pkglib_path(my_exec_path, pkglib_path);
-}
-
void
SwitchToSharedLatch(void)
{
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 41867dc14ac..53f6178a8a9 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -132,10 +132,11 @@ typedef struct ClientConnectionInfo
typedef struct Port
{
pgsocket sock; /* File descriptor */
- bool noblock; /* is the socket in non-blocking mode? */
- ProtocolVersion proto; /* FE/BE protocol version */
SockAddr laddr; /* local addr (postmaster) */
SockAddr raddr; /* remote addr (client) */
+
+ bool noblock; /* is the socket in non-blocking mode? */
+ ProtocolVersion proto; /* FE/BE protocol version */
char *remote_host; /* name (or ip addr) of remote host */
char *remote_hostname; /* name (not ip addr) of remote host, if
* available */
@@ -218,11 +219,12 @@ typedef struct Port
* ClientSocket holds a socket for an accepted connection, along with the
* information about the endpoints.
*/
-typedef struct ClientSocket {
+struct ClientSocket {
pgsocket sock; /* File descriptor */
SockAddr laddr; /* local addr (postmaster) */
SockAddr raddr; /* remote addr (client) */
-} ClientSocket;
+};
+typedef struct ClientSocket ClientSocket;
#ifdef USE_SSL
/*
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 14bd574fc24..0b41254920c 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -317,7 +317,6 @@ extern int trace_recovery(int trace_level);
extern PGDLLIMPORT char *DatabasePath;
/* now in utils/init/miscinit.c */
-extern void InitPostmasterChild(void);
extern void InitStandaloneProcess(const char *argv0);
extern void InitProcessLocalLatch(void);
extern void SwitchToSharedLatch(void);
diff --git a/src/include/postmaster/autovacuum.h b/src/include/postmaster/autovacuum.h
index 65afd1ea1e8..b6b338a4971 100644
--- a/src/include/postmaster/autovacuum.h
+++ b/src/include/postmaster/autovacuum.h
@@ -55,20 +55,14 @@ extern bool IsAutoVacuumWorkerProcess(void);
#define IsAnyAutoVacuumProcess() \
(IsAutoVacuumLauncherProcess() || IsAutoVacuumWorkerProcess())
-/* Functions to start autovacuum process, called from postmaster */
+/* called from postmaster at server startup */
extern void autovac_init(void);
-extern int StartAutoVacLauncher(void);
-extern int StartAutoVacWorker(void);
/* called from postmaster when a worker could not be forked */
extern void AutoVacWorkerFailed(void);
-#ifdef EXEC_BACKEND
-extern void AutoVacLauncherMain(int argc, char *argv[]) pg_attribute_noreturn();
-extern void AutoVacWorkerMain(int argc, char *argv[]) pg_attribute_noreturn();
-extern void AutovacuumWorkerIAm(void);
-extern void AutovacuumLauncherIAm(void);
-#endif
+extern void AutoVacLauncherMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
+extern void AutoVacWorkerMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
extern bool AutoVacuumRequestWork(AutoVacuumWorkItemType type,
Oid relationId, BlockNumber blkno);
diff --git a/src/include/postmaster/auxprocess.h b/src/include/postmaster/auxprocess.h
index 5c2d6527ff6..75394ca0155 100644
--- a/src/include/postmaster/auxprocess.h
+++ b/src/include/postmaster/auxprocess.h
@@ -13,8 +13,6 @@
#ifndef AUXPROCESS_H
#define AUXPROCESS_H
-#include "miscadmin.h"
-
-extern void AuxiliaryProcessMain(AuxProcType auxtype) pg_attribute_noreturn();
+extern void AuxiliaryProcessInit(void);
#endif /* AUXPROCESS_H */
diff --git a/src/include/postmaster/bgworker_internals.h b/src/include/postmaster/bgworker_internals.h
index 7fa7ee2d878..c29c37df9cc 100644
--- a/src/include/postmaster/bgworker_internals.h
+++ b/src/include/postmaster/bgworker_internals.h
@@ -54,7 +54,7 @@ extern void BackgroundWorkerStopNotifications(pid_t pid);
extern void ForgetUnstartedBackgroundWorkers(void);
extern void ResetBackgroundWorkerCrashTimes(void);
-/* Function to start a background worker, called from postmaster.c */
-extern void StartBackgroundWorker(void) pg_attribute_noreturn();
+/* Entry point of a background worker process, called from postmaster.c */
+extern void BackgroundWorkerMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
#endif /* BGWORKER_INTERNALS_H */
diff --git a/src/include/postmaster/bgwriter.h b/src/include/postmaster/bgwriter.h
index a66722873f4..ee54fc401ef 100644
--- a/src/include/postmaster/bgwriter.h
+++ b/src/include/postmaster/bgwriter.h
@@ -27,8 +27,8 @@ extern PGDLLIMPORT int CheckPointTimeout;
extern PGDLLIMPORT int CheckPointWarning;
extern PGDLLIMPORT double CheckPointCompletionTarget;
-extern void BackgroundWriterMain(void) pg_attribute_noreturn();
-extern void CheckpointerMain(void) pg_attribute_noreturn();
+extern void BackgroundWriterMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
+extern void CheckpointerMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
extern void RequestCheckpoint(int flags);
extern void CheckpointWriteDelay(int flags, double progress);
diff --git a/src/include/postmaster/fork_process.h b/src/include/postmaster/fork_process.h
deleted file mode 100644
index 12decc8133b..00000000000
--- a/src/include/postmaster/fork_process.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fork_process.h
- * Exports from postmaster/fork_process.c.
- *
- * Copyright (c) 1996-2023, PostgreSQL Global Development Group
- *
- * src/include/postmaster/fork_process.h
- *
- *-------------------------------------------------------------------------
- */
-#ifndef FORK_PROCESS_H
-#define FORK_PROCESS_H
-
-extern pid_t fork_process(void);
-
-#endif /* FORK_PROCESS_H */
diff --git a/src/include/postmaster/pgarch.h b/src/include/postmaster/pgarch.h
index 3bd4fac71e5..577fc14e1d0 100644
--- a/src/include/postmaster/pgarch.h
+++ b/src/include/postmaster/pgarch.h
@@ -29,7 +29,7 @@
extern Size PgArchShmemSize(void);
extern void PgArchShmemInit(void);
extern bool PgArchCanRestart(void);
-extern void PgArchiverMain(void) pg_attribute_noreturn();
+extern void PgArchiverMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
extern void PgArchWakeup(void);
extern void PgArchForceDirScan(void);
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 3b3889c58c0..bb0b15fcf32 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -58,14 +58,48 @@ extern int MaxLivePostmasterChildren(void);
extern bool PostmasterMarkPIDForWorkerNotify(int);
-#ifdef EXEC_BACKEND
-extern pid_t postmaster_forkexec(int argc, char *argv[]);
-extern void SubPostmasterMain(int argc, char *argv[]) pg_attribute_noreturn();
+extern void BackendMain(char *startup_data, size_t startup_data_len);
+#ifdef EXEC_BACKEND
extern Size ShmemBackendArraySize(void);
extern void ShmemBackendArrayAllocation(void);
#endif
+/* in process_start.c */
+
+/* this better match the list in process_start.c */
+typedef enum PostmasterChildType {
+ PMC_BACKEND = 0,
+ PMC_AV_LAUNCHER,
+ PMC_AV_WORKER,
+ PMC_BGWORKER,
+ PMC_SYSLOGGER,
+
+ /*
+ * so-called "aux processes". These access shared memory, but are not attached to
+ * any particular database. Only one of each of these can be running at a time.
+ */
+ PMC_STARTUP,
+ PMC_BGWRITER,
+ PMC_ARCHIVER,
+ PMC_CHECKPOINTER,
+ PMC_WAL_WRITER,
+ PMC_WAL_RECEIVER,
+} PostmasterChildType;
+
+typedef void (*ChildEntryPoint) (char *startup_data, size_t startup_data_len);
+
+/* defined in libpq-be.h */
+extern struct ClientSocket *MyClientSocket;
+
+extern pid_t postmaster_child_launch(PostmasterChildType child_type, char *startup_data, size_t startup_data_len, struct ClientSocket *sock);
+
+#ifdef EXEC_BACKEND
+extern void SubPostmasterMain(int argc, char *argv[]) pg_attribute_noreturn();
+#endif
+
+const char *PostmasterChildName(PostmasterChildType child_type);
+
/*
* 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
diff --git a/src/include/postmaster/startup.h b/src/include/postmaster/startup.h
index 6a2e4c4526b..ec885063aab 100644
--- a/src/include/postmaster/startup.h
+++ b/src/include/postmaster/startup.h
@@ -26,7 +26,7 @@
extern PGDLLIMPORT int log_startup_progress_interval;
extern void HandleStartupProcInterrupts(void);
-extern void StartupProcessMain(void) pg_attribute_noreturn();
+extern void StartupProcessMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
extern void PreRestoreCommand(void);
extern void PostRestoreCommand(void);
extern bool IsPromoteSignaled(void);
diff --git a/src/include/postmaster/syslogger.h b/src/include/postmaster/syslogger.h
index 34da778f1ef..7dc41b30e7c 100644
--- a/src/include/postmaster/syslogger.h
+++ b/src/include/postmaster/syslogger.h
@@ -86,9 +86,7 @@ extern int SysLogger_Start(void);
extern void write_syslogger_file(const char *buffer, int count, int destination);
-#ifdef EXEC_BACKEND
-extern void SysLoggerMain(int argc, char *argv[]) pg_attribute_noreturn();
-#endif
+extern void SysLoggerMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
extern bool CheckLogrotateSignal(void);
extern void RemoveLogrotateSignalFiles(void);
diff --git a/src/include/postmaster/walwriter.h b/src/include/postmaster/walwriter.h
index 6eba7ad79cf..99b7cc07fb2 100644
--- a/src/include/postmaster/walwriter.h
+++ b/src/include/postmaster/walwriter.h
@@ -18,6 +18,6 @@
extern PGDLLIMPORT int WalWriterDelay;
extern PGDLLIMPORT int WalWriterFlushAfter;
-extern void WalWriterMain(void) pg_attribute_noreturn();
+extern void WalWriterMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
#endif /* _WALWRITER_H */
diff --git a/src/include/replication/walreceiver.h b/src/include/replication/walreceiver.h
index 281626fa6f5..92dead4db7b 100644
--- a/src/include/replication/walreceiver.h
+++ b/src/include/replication/walreceiver.h
@@ -457,7 +457,7 @@ walrcv_clear_result(WalRcvExecResult *walres)
}
/* prototypes for functions in walreceiver.c */
-extern void WalReceiverMain(void) pg_attribute_noreturn();
+extern void WalReceiverMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
extern void ProcessWalRcvInterrupts(void);
extern void WalRcvForceReply(void);
--
2.30.2
view thread (2+ messages)
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: Refactoring backend fork+exec code
In-Reply-To: <[email protected]>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox