public inbox for [email protected]
help / color / mirror / Atom feedFrom: Heikki Linnakangas <[email protected]>
To: Andres Freund <[email protected]>
To: Tristan Partin <[email protected]>
Cc: pgsql-hackers <[email protected]>
Cc: Thomas Munro <[email protected]>
Subject: Re: Refactoring backend fork+exec code
Date: Wed, 11 Oct 2023 14:12:47 +0300
Message-ID: <[email protected]> (raw)
In-Reply-To: <[email protected]>
References: <[email protected]>
<[email protected]>
I updated this patch set, addressing some of the straightforward
comments from Tristan and Andres, and did some more cleanups, commenting
etc. Works on Windows now.
Replies to some of the individual comments below:
On 11/07/2023 00:07, Tristan Partin wrote:
>> @@ -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;
>
> Might be worth a sanity check that there wasn't any truncation into
> cacbuf, which is an impossibility as the code is written, but still
> useful for catching a future developer error.
>
> Is it worth adding a command line option at all instead of having the
> naked positional argument? It would help anybody who might read the
> command line what the seemingly random integer stands for.
+1. This gets refactored away in the last patch though. In the last
patch, I used a child process name instead of an integer precisely
because it looks nicer in "ps".
I wonder if we should add more command line arguments, just for
informational purposes. Autovacuum worker process could display the
database name it's connected to, for example. I don't know how important
the command line is on Windows, is it displayed by tools that people
care about?
On 11/07/2023 01:50, Andres Freund wrote:
> On 2023-06-18 14:22:33 +0300, Heikki Linnakangas wrote:
>> 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.
>
> What's the reason for this part?
The point is that with this commit, InitProcess() is called at same
place in EXEC_BACKEND mode and !EXEC_BACKEND. It feels more consistent
that way.
> ISTM that we'd really want to get away from plastering duplicated
> InitProcess() etc everywhere.
Sure, we could do more to reduce the duplication. I think this is a step
in the right direction, though.
>> 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.)
>
> I think there might be extensions accessing Port. Not sure if it's worth
> worrying about, but ...
That's OK. Port still exists, it's just created a little later. It will
be initialized by the time extensions might look at it.
>> +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},
>> +};
>
> I'd assign them with the PostmasterChildType as index, so there's no danger of
> getting out of order.
>
> const PMChildEntry entry_kinds = {
> [PMC_AV_LAUNCHER] = {"autovacuum launcher", AutoVacLauncherMain, true},
> ...
> }
>
> or such should work.
Nice, I didn't know about that syntax! Changed it that way.
> I'd also use designated initializers for the fields, it's otherwise hard to
> know what true means etc.
I think with one boolean and the struct declaration nearby, it's fine.
If this becomes more complex in the future, with more fields, I agree.
> I think it might be good to put more into array. If we e.g. knew whether a
> particular child type is a backend-like, and aux process or syslogger, we
> could avoid the duplicated InitAuxiliaryProcess(),
> MemoryContextDelete(PostmasterContext) etc calls everywhere.
I agree we could do more refactoring here. I don't agree with adding
more to this struct though. I'm trying to limit the code in
launch_backend.c to hiding the differences between EXEC_BACKEND and
!EXEC_BACKEND. In EXEC_BACKEND mode, it restores the child process to
the same state as it is after fork() in !EXEC_BACKEND mode. Any other
initialization steps belong elsewhere.
Some of the steps between InitPostmasterChild() and the *Main()
functions could probably be moved around and refactored. I didn't think
hard about that. I think that can be done separately as follow-up patch.
>> +/* Save critical backend variables into the BackendParameters struct */
>> +#ifndef WIN32
>> +static bool
>> +save_backend_variables(BackendParameters *param, ClientSocket *client_sock)
>> +#else
>
> There's so much of this kind of thing. Could we hide it in a struct or such
> instead of needing ifdefs everywhere?
A lot of #ifdefs you mean? I agree launch_backend.c has a lot of those.
I haven't come up with any good ideas on reducing them, unfortunately.
--
Heikki Linnakangas
Neon (https://neon.tech)
Attachments:
[text/x-patch] v2-0001-Pass-background-worker-entry-in-the-parameter-fil.patch (5.6K, ../[email protected]/2-v2-0001-Pass-background-worker-entry-in-the-parameter-fil.patch)
download | inline diff:
From f48172bb716dede354ab6cd68f84779a6e63678a Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Wed, 11 Oct 2023 13:35:20 +0300
Subject: [PATCH v2 1/6] 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.
Reviewed-by: Tristan Partin, Andres Freund
Discussion: https://www.postgresql.org/message-id/[email protected]
---
src/backend/postmaster/bgworker.c | 21 -----------
src/backend/postmaster/postmaster.c | 39 ++++++++++++++-------
src/include/postmaster/bgworker_internals.h | 4 ---
3 files changed, 26 insertions(+), 38 deletions(-)
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 48a9924527e..b3c2e65ba9f 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -631,27 +631,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 282e6486948..e55f3a152aa 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);
@@ -4831,7 +4833,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();
@@ -4962,10 +4964,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;
@@ -4978,11 +4978,7 @@ 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);
-
- BackgroundWorkerMain();
+ StartBackgroundWorker();
}
if (strcmp(argv[1], "--forklog") == 0)
{
@@ -5634,13 +5630,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;
@@ -5649,7 +5646,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
@@ -5690,7 +5691,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
@@ -6090,6 +6091,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;
}
@@ -6318,6 +6324,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 09df054fcce..323f1e07291 100644
--- a/src/include/postmaster/bgworker_internals.h
+++ b/src/include/postmaster/bgworker_internals.h
@@ -57,8 +57,4 @@ extern void ResetBackgroundWorkerCrashTimes(void);
/* Entry point for background worker processes */
extern void BackgroundWorkerMain(void) pg_attribute_noreturn();
-#ifdef EXEC_BACKEND
-extern BackgroundWorker *BackgroundWorkerEntry(int slotno);
-#endif
-
#endif /* BGWORKER_INTERNALS_H */
--
2.39.2
[text/x-patch] v2-0002-Refactor-CreateSharedMemoryAndSemaphores.patch (10.2K, ../[email protected]/3-v2-0002-Refactor-CreateSharedMemoryAndSemaphores.patch)
download | inline diff:
From 03f53ab9208240f45b7ec90bc435c81fe1140671 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Wed, 11 Oct 2023 13:36:16 +0300
Subject: [PATCH v2 2/6] Refactor CreateSharedMemoryAndSemaphores.
The order of process initialization steps is now more consistent
between !EXEC_BACKEND and EXEC_BACKEND modes. InitProcess() is called
at the same place in either mode. In EXEC_BACKEND mode, there's an
extra AttachSharedMemoryAndSemaphores() call, which is a no-op in
!EXEC_BACKEND mode. This reduces the number of "#ifdef EXEC_BACKEND"
blocks.
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.
Reviewed-by: Tristan Partin, Andres Freund
Discussion: https://www.postgresql.org/message-id/[email protected]
---
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/replication/walreceiver.c | 3 +-
src/backend/storage/ipc/ipci.c | 12 +++++++
src/backend/storage/lmgr/proc.c | 2 +-
src/include/storage/ipc.h | 1 +
8 files changed, 44 insertions(+), 63 deletions(-)
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 327ea0d45ad..17f2b36b28a 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 b3c2e65ba9f..15435965bf8 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -810,14 +810,13 @@ BackgroundWorkerMain(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 e55f3a152aa..13ebf7ac4de 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -4097,15 +4097,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);
}
@@ -4416,6 +4407,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.)
@@ -4911,12 +4911,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 */
}
@@ -4929,12 +4923,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 */
}
@@ -4943,12 +4931,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)
@@ -4956,12 +4938,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)
@@ -4972,12 +4948,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/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index feff7094351..073668d4f2e 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -194,7 +194,7 @@ WalReceiverMain(void)
TimeLineID startpointTLI;
TimeLineID primaryTLI;
bool first_stream;
- WalRcvData *walrcv = WalRcv;
+ WalRcvData *walrcv;
TimestampTz now;
char *err;
char *sender_host = NULL;
@@ -204,6 +204,7 @@ WalReceiverMain(void)
* WalRcv should be set up already (if we are a backend, we inherit this
* by fork() or EXEC_BACKEND mechanism from the postmaster).
*/
+ walrcv = WalRcv;
Assert(walrcv != NULL);
/*
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index a3d8eacb8dc..4f31ceca073 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -171,6 +171,18 @@ 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 */
+ Assert(MyProc != NULL);
+
+ /* 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 5b663a2997c..c1d259b1405 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.39.2
[text/x-patch] v2-0003-Pass-CAC-as-argument-to-backend-process.patch (5.4K, ../[email protected]/4-v2-0003-Pass-CAC-as-argument-to-backend-process.patch)
download | inline diff:
From 0473f732fc3ca894dfdf6e98297582ef10fea7d8 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Wed, 11 Oct 2023 13:38:00 +0300
Subject: [PATCH v2 3/6] 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.
Reviewed-by: Tristan Partin, Andres Freund
Discussion: https://www.postgresql.org/message-id/[email protected]
---
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 13ebf7ac4de..936828df010 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -418,7 +418,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);
@@ -475,7 +486,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 */
@@ -4037,6 +4048,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
@@ -4068,8 +4080,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
@@ -4083,7 +4095,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 */
@@ -4095,7 +4107,7 @@ BackendStartup(Port *port)
ClosePostmasterPorts(false);
/* Perform additional initialization and collect startup packet */
- BackendInitialize(port);
+ BackendInitialize(port, cac);
/* And run the backend */
BackendRun(port);
@@ -4182,7 +4194,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;
@@ -4315,7 +4327,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,
@@ -4460,15 +4472,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));
@@ -4872,7 +4888,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
@@ -4906,7 +4925,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 a0b74c8095f..335cb2de44a 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.39.2
[text/x-patch] v2-0004-Remove-ConnCreate-and-ConnFree-and-allocate-Port-.patch (3.8K, ../[email protected]/5-v2-0004-Remove-ConnCreate-and-ConnFree-and-allocate-Port-.patch)
download | inline diff:
From 145fe47c788b0e02d4b0e827cfaa32fe9b04dfe3 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Wed, 11 Oct 2023 13:38:06 +0300
Subject: [PATCH v2 4/6] 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.
XXX: This should perhaps be squashed with the next commit. It changes
how the Port structure is allocated again. But maybe it's easier to
review separately.
Reviewed-by: Tristan Partin, Andres Freund
Discussion: https://www.postgresql.org/message-id/[email protected]
---
src/backend/postmaster/postmaster.c | 68 +++++------------------------
src/backend/tcop/postgres.c | 2 +-
2 files changed, 11 insertions(+), 59 deletions(-)
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 936828df010..ff52b9acf35 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -399,8 +399,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);
@@ -1772,20 +1770,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);
}
}
@@ -2447,50 +2443,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
*
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index f3c9f1f9bab..dca5eb78297 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4217,7 +4217,7 @@ PostgresMain(const char *dbname, const char *username)
/*
* If the PostmasterContext is still around, recycle the space; we don't
* need it anymore after InitPostgres completes. Note this does not trash
- * *MyProcPort, because ConnCreate() allocated that space with malloc()
+ * *MyProcPort, because that space is allocated in stack
* ... else we'd need to copy the Port data first. Also, subsidiary data
* such as the username isn't lost either; see ProcessStartupPacket().
*/
--
2.39.2
[text/x-patch] v2-0005-Introduce-ClientSocket-rename-some-funcs.patch (25.9K, ../[email protected]/6-v2-0005-Introduce-ClientSocket-rename-some-funcs.patch)
download | inline diff:
From abfefd9ee0112c095d49d3979c04d9cb16af94eb Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Wed, 11 Oct 2023 13:38:10 +0300
Subject: [PATCH v2 5/6] 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.)
Reviewed-by: Tristan Partin, Andres Freund
Discussion: https://www.postgresql.org/message-id/[email protected]
---
src/backend/libpq/pqcomm.c | 93 +++++++++--------
src/backend/postmaster/postmaster.c | 149 +++++++++++++++-------------
src/backend/tcop/postgres.c | 5 +-
src/include/libpq/libpq-be.h | 19 ++--
src/include/libpq/libpq.h | 6 +-
src/tools/pgindent/typedefs.list | 1 +
6 files changed, 149 insertions(+), 124 deletions(-)
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 522584e5978..93ca3b1b8c1 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
@@ -319,7 +319,7 @@ socket_close(int code, Datum arg)
* RETURNS: STATUS_OK or STATUS_ERROR
*/
int
-StreamServerPort(int family, const char *hostName, unsigned short portNumber,
+ListenServerPort(int family, const char *hostName, unsigned short portNumber,
const char *unixSocketDir,
pgsocket ListenSockets[], int *NumListenSockets, int MaxListen)
{
@@ -685,8 +685,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().
@@ -694,13 +695,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(),
@@ -718,10 +719,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")));
@@ -729,7 +730,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
@@ -740,7 +741,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,
@@ -749,7 +750,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,
@@ -781,7 +782,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,
@@ -791,7 +792,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,
@@ -800,13 +801,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);
@@ -814,24 +836,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, "could not close client or listen socket: %m");
+ return port;
}
/*
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index ff52b9acf35..1b1a44e3ea8 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -427,15 +427,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);
@@ -484,8 +484,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
@@ -504,8 +504,8 @@ typedef int InheritableSocket;
*/
typedef struct
{
- Port port;
- InheritableSocket portsocket;
+ ClientSocket client_sock;
+ InheritableSocket serialized_sock;
char DataDir[MAXPGPATH];
int32 MyCancelKey;
int MyPMChildSlot;
@@ -553,13 +553,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
@@ -1219,14 +1219,14 @@ 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,
ListenSockets,
&NumListenSockets,
MAXLISTEN);
else
- status = StreamServerPort(AF_UNSPEC, curhost,
+ status = ListenServerPort(AF_UNSPEC, curhost,
(unsigned short) PostPortNumber,
NULL,
ListenSockets,
@@ -1320,7 +1320,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,
ListenSockets,
@@ -1500,7 +1500,7 @@ CloseServerPorts(int status, Datum arg)
* condition if a new postmaster wants to re-use the TCP port number.
*/
for (i = 0; i < NumListenSockets; i++)
- StreamClose(ListenSockets[i]);
+ closesocket(ListenSockets[i]);
NumListenSockets = 0;
/*
@@ -1770,18 +1770,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");
+ }
}
}
@@ -2146,11 +2148,7 @@ retry1:
/*
* Now fetch parameters out of startup packet and save them into the Port
- * structure. All data structures attached to the Port struct must be
- * allocated in TopMemoryContext so that they will remain available in a
- * running backend (even after PostmasterContext is destroyed). We need
- * not worry about leaking this storage on failure, since we aren't in the
- * postmaster process anymore.
+ * structure.
*/
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
@@ -2286,7 +2284,7 @@ retry1:
port->database_name[0] = '\0';
/*
- * Done putting stuff in TopMemoryContext.
+ * Done filling the Port structure
*/
MemoryContextSwitchTo(oldcontext);
@@ -2490,7 +2488,10 @@ ClosePostmasterPorts(bool am_syslogger)
if (ListenSockets)
{
for (int i = 0; i < NumListenSockets; i++)
- StreamClose(ListenSockets[i]);
+ {
+ if (closesocket(ListenSockets[i]) != 0)
+ elog(LOG, "could not close listen socket: %m");
+ }
pfree(ListenSockets);
}
NumListenSockets = 0;
@@ -3996,7 +3997,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;
@@ -4047,7 +4048,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 */
@@ -4059,10 +4060,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 */
@@ -4077,14 +4078,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
@@ -4111,7 +4112,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;
@@ -4122,13 +4123,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);
}
@@ -4146,16 +4147,28 @@ 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 the Port structure.
+ *
+ * The Port structure and all data structures attached to it are allocated
+ * in TopMemoryContext, so that they survive into PostgresMain execution.
+ * We need not worry about leaking this storage on failure, since we
+ * aren't in the postmaster process anymore.
+ */
+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+ port = StreamConnection(client_sock);
MyProcPort = port;
+ MemoryContextSwitchTo(oldcontext);
/* Tell fd.c about the long-lived FD associated with the port */
ReserveExternalFD();
@@ -4216,8 +4229,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)
@@ -4248,7 +4262,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
@@ -4369,7 +4384,7 @@ 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
@@ -4386,7 +4401,7 @@ BackendRun(Port *port)
*/
MemoryContextSwitchTo(TopMemoryContext);
- PostgresMain(port->database_name, port->user_name);
+ PostgresMain(MyProcPort->database_name, MyProcPort->user_name);
}
@@ -4407,11 +4422,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);
}
/*
@@ -4424,7 +4439,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;
@@ -4440,7 +4455,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
@@ -4452,7 +4467,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;
@@ -4460,7 +4475,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 */
@@ -4758,7 +4773,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;
@@ -4772,8 +4787,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);
@@ -4877,13 +4892,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)
{
@@ -5965,15 +5980,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);
@@ -6135,7 +6150,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;
@@ -6202,15 +6217,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/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index dca5eb78297..087a1b43d7a 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4216,10 +4216,7 @@ PostgresMain(const char *dbname, const char *username)
/*
* If the PostmasterContext is still around, recycle the space; we don't
- * need it anymore after InitPostgres completes. Note this does not trash
- * *MyProcPort, because that space is allocated in stack
- * ... else we'd need to copy the Port data first. Also, subsidiary data
- * such as the username isn't lost either; see ProcessStartupPacket().
+ * need it anymore after InitPostgres completes.
*/
if (PostmasterContext)
{
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 335cb2de44a..9b6d8fc5571 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 a6104d8cd02..889e86a7f61 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 *NumListenSockets, 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);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 8de90c49585..78a9e0ebbc8 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -380,6 +380,7 @@ ClientCertMode
ClientCertName
ClientConnectionInfo
ClientData
+ClientSocket
ClonePtrType
ClosePortalStmt
ClosePtrType
--
2.39.2
[text/x-patch] v2-0006-Refactor-postmaster-child-process-launching.patch (137.5K, ../[email protected]/7-v2-0006-Refactor-postmaster-child-process-launching.patch)
download | inline diff:
From 2d1a5a1b20e94911228143ae68da7ee0210de424 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Wed, 11 Oct 2023 13:38:42 +0300
Subject: [PATCH v2 6/6] Refactor postmaster child process launching
- Move code related to launching backend processes to new source file,
launch_backend.c
- Introduce new postmaster_child_launch() function that deals with the
differences between EXEC_BACKEND and fork mode.
- Refactor the mechanism of passing information 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.
Reviewed-by: Tristan Partin, Andres Freund
Discussion: https://www.postgresql.org/message-id/[email protected]
---
src/backend/postmaster/Makefile | 2 +-
src/backend/postmaster/autovacuum.c | 173 +--
src/backend/postmaster/auxprocess.c | 72 +-
src/backend/postmaster/bgworker.c | 14 +-
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 | 1272 +++++++++++++++
src/backend/postmaster/meson.build | 2 +-
src/backend/postmaster/pgarch.c | 9 +-
src/backend/postmaster/postmaster.c | 1557 +++----------------
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 | 9 +-
src/backend/storage/ipc/shmem.c | 2 +
src/backend/utils/init/globals.c | 1 +
src/backend/utils/init/miscinit.c | 133 --
src/include/libpq/libpq-be.h | 11 +-
src/include/miscadmin.h | 1 -
src/include/postmaster/autovacuum.h | 12 +-
src/include/postmaster/auxprocess.h | 4 +-
src/include/postmaster/bgworker_internals.h | 2 +-
src/include/postmaster/bgwriter.h | 4 +-
src/include/postmaster/fork_process.h | 17 -
src/include/postmaster/pgarch.h | 2 +-
src/include/postmaster/postmaster.h | 44 +-
src/include/postmaster/startup.h | 2 +-
src/include/postmaster/syslogger.h | 4 +-
src/include/postmaster/walwriter.h | 2 +-
src/include/replication/walreceiver.h | 2 +-
src/tools/pgindent/typedefs.list | 2 +
33 files changed, 1789 insertions(+), 2004 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 17f2b36b28a..a6ffe7749be 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..5c2b628bf9d 100644
--- a/src/backend/postmaster/auxprocess.c
+++ b/src/backend/postmaster/auxprocess.c
@@ -46,45 +46,23 @@ AuxProcType MyAuxProcType = NotAnAuxProcess; /* declared in miscadmin.h */
/*
- * AuxiliaryProcessMain
+ * AuxiliaryProcessInit
*
- * The main entry point for auxiliary processes, such as the bgwriter,
+ * Common initialization code for auxiliary processes, such as the bgwriter,
* walwriter, walreceiver, bootstrapper and the shared memory checker code.
- *
- * 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);
@@ -125,7 +103,6 @@ AuxiliaryProcessMain(AuxProcType auxtype)
*/
CreateAuxProcessResourceOwner();
-
/* Initialize backend status information */
pgstat_beinit();
pgstat_bestart();
@@ -134,37 +111,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 15435965bf8..9910691255e 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -723,17 +723,27 @@ bgworker_die(SIGNAL_ARGS)
* Main entry point for background worker processes.
*/
void
-BackgroundWorkerMain(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 f2e4f23d9fc..b8b505b25fd 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..fac658ba687
--- /dev/null
+++ b/src/backend/postmaster/launch_backend.c
@@ -0,0 +1,1272 @@
+/*-------------------------------------------------------------------------
+ *
+ * launch_backend.c
+ * Functions for launching backends and other postmaster child
+ * processes.
+ *
+ * On Unix systems, a new child process is launched with fork(). It inherits
+ * all the global variables and data structures that had been initialized in
+ * the postmaster. After forking, the child process closes the file
+ * descriptors that are not needed in the child process, and sets up the
+ * mechanism to detect death of the parent postmaster process, etc. After
+ * that, it calls the right Main function depending on the kind of child
+ * process.
+ *
+ * In EXEC_BACKEND mode, which is used on Windows but can be enabled on other
+ * platforms for testing, the child process is launched by fork() + exec() (or
+ * CreateProcess() on Windows). It does not inherit the state from the
+ * postmaster, so it needs to re-attach to the shared memory, re-initialize
+ * global variables, re-load the config file etc.
+ *
+ *
+ * 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 "common/file_utils.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;
+
+ size_t startup_data_len;
+ /* startup data follows */
+ char startup_data[FLEXIBLE_ARRAY_MEMBER];
+} BackendParameters;
+
+#define SizeOfBackendParameters(startup_data_len) (offsetof(BackendParameters, startup_data) + startup_data_len)
+
+static void read_backend_variables(char *id, char **startup_data, size_t *startup_data_len);
+static void restore_backend_variables(BackendParameters *param);
+
+static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock,
+#ifdef WIN32
+ HANDLE childProcess, pid_t childPid,
+#endif
+ char *startup_data, size_t startup_data_len);
+
+#endif /* EXEC_BACKEND */
+
+static void InitPostmasterChild(bool am_syslogger);
+
+
+#ifdef EXEC_BACKEND
+static pid_t internal_forkexec(const char *main_fn_name, char *startup_data, size_t startup_data_len, ClientSocket *client_sock);
+#endif /* EXEC_BACKEND */
+
+#ifndef WIN32
+static pid_t fork_process(void);
+#endif
+
+/*
+ * Information needed to launch different kinds of child processes.
+ */
+static const struct
+{
+ const char *name;
+ void (*main_fn) (char *startup_data, size_t startup_data_len);
+ bool shmem_attach;
+} entry_kinds[] = {
+ [PMC_BACKEND] = {"backend", BackendMain, true},
+
+ [PMC_AV_LAUNCHER] = {"autovacuum launcher", AutoVacLauncherMain, true},
+ [PMC_AV_WORKER] = {"autovacuum worker", AutoVacWorkerMain, true},
+ [PMC_BGWORKER] = {"bgworker", BackgroundWorkerMain, true},
+ [PMC_SYSLOGGER] = {"syslogger", SysLoggerMain, false},
+
+ [PMC_STARTUP] = {"startup", StartupProcessMain, true},
+ [PMC_BGWRITER] = {"bgwriter", BackgroundWriterMain, true},
+ [PMC_ARCHIVER] = {"archiver", PgArchiverMain, true},
+ [PMC_CHECKPOINTER] = {"checkpointer", CheckpointerMain, true},
+ [PMC_WAL_WRITER] = {"wal_writer", WalWriterMain, true},
+ [PMC_WAL_RECEIVER] = {"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 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.
+ *
+ * 'startup_data' is an optional contiguous chunk of data that is passed to
+ * 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 (in the Main function), 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].main_fn(startup_data, startup_data_len);
+ Assert(false);
+ }
+#endif /* EXEC_BACKEND */
+ return pid;
+}
+
+#ifndef WIN32
+/*
+ * Wrapper for fork(). Performs initialization steps that are the same in
+ * !EXEC_BACKEND and EXEC_BACKEND modes.
+ *
+ * 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.
+ */
+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];
+ size_t paramsz;
+ BackendParameters *param;
+ FILE *fp;
+ char *argv[4];
+ char forkav[MAXPGPATH];
+
+ paramsz = SizeOfBackendParameters(startup_data_len);
+ param = palloc(paramsz);
+ if (!save_backend_variables(param, client_sock, startup_data, startup_data_len))
+ {
+ pfree(param);
+ 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(param, paramsz, 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 --forkchild 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;
+ char cmdLine[MAXPGPATH * 2];
+ HANDLE paramHandle;
+ BackendParameters *param;
+ SECURITY_ATTRIBUTES sa;
+ size_t paramsz;
+ char paramHandleStr[32];
+ int l;
+
+ paramsz = SizeOfBackendParameters(startup_data_len);
+
+ /* 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,
+ paramsz,
+ 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, paramsz);
+ if (!param)
+ {
+ ereport(LOG,
+ (errmsg("could not map backend parameter memory: error code %lu",
+ GetLastError())));
+ CloseHandle(paramHandle);
+ return -1;
+ }
+
+ /* Format the cmd line */
+#ifdef _WIN64
+ sprintf(paramHandleStr, "%llu", (LONG_PTR) paramHandle);
+#else
+ sprintf(paramHandleStr, "%lu", (DWORD) paramHandle);
+#endif
+ l = snprintf(cmdLine, sizeof(cmdLine) - 1, "\"%s\" --forkchild=\"%s\" %s",
+ postgres_exec_path, entry_name, paramHandleStr);
+ if (l >= sizeof(cmdLine))
+ {
+ 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, startup_data, startup_data_len))
+ {
+ /*
+ * 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;
+ }
+
+ pgwin32_register_deadchild_callback(pi.hProcess, pi.dwProcessId);
+
+ /* 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 "--forkchild=<name>",
+ * where <name> 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.
+ */
+void
+SubPostmasterMain(int argc, char *argv[])
+{
+ PostmasterChildType child_type;
+ char *startup_data;
+ size_t startup_data_len;
+ char *entry_name;
+ bool found = false;
+
+ /* 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)
+ elog(FATAL, "invalid subpostmaster invocation (--forkchild argument missing)");
+ entry_name = argv[1] + 12;
+ 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].main_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 */
+static bool
+save_backend_variables(BackendParameters *param, ClientSocket *client_sock,
+#ifdef WIN32
+ HANDLE childProcess, pid_t childPid,
+#endif
+ char *startup_data, size_t startup_data_len)
+{
+ 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);
+
+ param->startup_data_len = startup_data_len;
+ memcpy(param->startup_data, startup_data, startup_data_len);
+
+ 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 */
+ *startup_data_len = param.startup_data_len;
+ if (param.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 *filep;
+
+#ifdef _WIN64
+ paramHandle = (HANDLE) _atoi64(id);
+#else
+ paramHandle = (HANDLE) atol(id);
+#endif
+ filep = MapViewOfFile(paramHandle, FILE_MAP_READ, 0, 0, 0);
+ if (!filep)
+ {
+ write_stderr("could not map view of backend variables: error code %lu\n",
+ GetLastError());
+ exit(1);
+ }
+
+ memcpy(¶m, filep, sizeof(BackendParameters));
+
+ /* read startup data */
+ *startup_data_len = param.startup_data_len;
+ if (param.startup_data_len > 0)
+ {
+ *startup_data = palloc(param.startup_data_len);
+ memcpy(*startup_data, filep->startup_data, param.startup_data_len);
+ }
+ else
+ *startup_data = NULL;
+
+ if (!UnmapViewOfFile(filep))
+ {
+ 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 1b1a44e3ea8..10a4d9540c4 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -2,9 +2,9 @@
*
* postmaster.c
* This program acts as a clearing house for requests to the
- * POSTGRES system. Frontend programs send a startup message
- * to the Postmaster and the postmaster uses the info in the
- * message to setup a backend process.
+ * POSTGRES system. Frontend programs connect to the Postmaster,
+ * and postmaster forks a new backend process to handle the
+ * connection.
*
* The postmaster also manages system-wide operations such as
* startup and shutdown. The postmaster itself doesn't do those
@@ -110,7 +110,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"
@@ -132,10 +131,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
@@ -188,7 +183,7 @@ typedef struct bkend
static dlist_head BackendList = DLIST_STATIC_INIT(BackendList);
#ifdef EXEC_BACKEND
-static Backend *ShmemBackendArray;
+Backend *ShmemBackendArray;
#endif
BackgroundWorker *MyBgworkerEntry = NULL;
@@ -413,7 +408,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);
@@ -428,7 +423,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);
@@ -449,7 +443,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);
@@ -484,95 +478,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
@@ -3989,6 +3907,11 @@ TerminateChildren(int signal)
signal_child(PgArchPID, signal);
}
+typedef struct BackendStartupInfo
+{
+ CAC_state canAcceptConnections;
+} BackendStartupInfo;
+
/*
* BackendStartup -- start backend process
*
@@ -4001,7 +3924,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
@@ -4030,11 +3953,10 @@ 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->dead_end = (info.canAcceptConnections != CAC_OK);
+ bn->cancel_key = MyCancelKey;
/*
* Unless it's a dead_end child, assign it a child slot number
@@ -4047,26 +3969,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 */
@@ -4103,6 +4006,61 @@ 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.
+ */
+ 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
@@ -4378,732 +4336,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();
}
/*
@@ -5240,93 +4627,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;
}
@@ -5584,32 +4901,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.
@@ -5646,65 +4937,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;
-
- BackgroundWorkerMain();
+ /* 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;
}
/*
@@ -5956,351 +5214,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)
{
@@ -6422,6 +5335,32 @@ pgwin32_deadchild_callback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
/* Queue SIGCHLD signal. */
pg_queue_signal(SIGCHLD);
}
+
+/*
+ * 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().
+ */
+void
+pgwin32_register_deadchild_callback(HANDLE procHandle, DWORD procId)
+{
+ win32_deadchild_waitinfo *childinfo;
+
+ childinfo = palloc(sizeof(win32_deadchild_waitinfo));
+ childinfo->procHandle = procHandle;
+ childinfo->procId = procId;
+
+ if (!RegisterWaitForSingleObject(&childinfo->waitHandle,
+ procHandle,
+ pgwin32_deadchild_callback,
+ childinfo,
+ INFINITE,
+ WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD))
+ ereport(FATAL,
+ (errmsg_internal("could not register process for wait: error code %lu",
+ GetLastError())));
+}
+
#endif /* WIN32 */
/*
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..de45b9565fe 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,19 @@ 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 +177,34 @@ 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 = (syslogger_startup_data *) startup_data;
+
+ Assert(startup_data_len == sizeof(*info));
+ 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 501910b4454..a4098c23b2b 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 073668d4f2e..0d4bdd976db 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,6 +201,12 @@ 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).
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/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 1e671c560c8..c80222112b3 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 9b6d8fc5571..959feeba025 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,13 @@ 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 1cc3712c0ff..a60323cefca 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 323f1e07291..4055d2f5626 100644
--- a/src/include/postmaster/bgworker_internals.h
+++ b/src/include/postmaster/bgworker_internals.h
@@ -55,6 +55,6 @@ extern void ForgetUnstartedBackgroundWorkers(void);
extern void ResetBackgroundWorkerCrashTimes(void);
/* Entry point for background worker processes */
-extern void BackgroundWorkerMain(void) pg_attribute_noreturn();
+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..1dbb5c991f5 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -58,14 +58,52 @@ 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 launch_backend.c */
+
+/* this better match the list in launch_backend.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;
+
+/* 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
+
+#ifdef WIN32
+extern void pgwin32_register_deadchild_callback(HANDLE procHandle, DWORD procId);
+#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);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 78a9e0ebbc8..ecc4ea0ee9c 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2111,6 +2111,7 @@ PortalStrategy
PostParseColumnRefHook
PostgresPollingStatusType
PostingItem
+PostmasterChildType
PreParseColumnRefHook
PredClass
PredIterInfo
@@ -3778,6 +3779,7 @@ substitute_actual_parameters_context
substitute_actual_srf_parameters_context
substitute_phv_relids_context
symbol
+syslogger_startup_data
tablespaceinfo
teSection
temp_tablespaces_extra
--
2.39.2
view thread (20+ messages) latest in thread
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], [email protected], [email protected], [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