Refactor postmaster child process launching

Introduce new postmaster_child_launch() function that deals with the
differences in EXEC_BACKEND 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 startup data along with all the global
variables. The contents of that blob depend 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/7a59b073-5b5b-151e-7ed3-8b01ff7ce9ef@iki.fi
This commit is contained in:
Heikki Linnakangas 2024-03-18 11:35:08 +02:00
parent f1baed18bc
commit aafc05de1b
28 changed files with 676 additions and 1008 deletions

View File

@ -85,7 +85,6 @@
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "pgstat.h" #include "pgstat.h"
#include "postmaster/autovacuum.h" #include "postmaster/autovacuum.h"
#include "postmaster/fork_process.h"
#include "postmaster/interrupt.h" #include "postmaster/interrupt.h"
#include "postmaster/postmaster.h" #include "postmaster/postmaster.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
@ -311,13 +310,6 @@ static WorkerInfo MyWorkerInfo = NULL;
/* PID of launcher, valid only in worker while shutting down */ /* PID of launcher, valid only in worker while shutting down */
int AutovacuumLauncherPid = 0; 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 Oid do_start_worker(void);
static void HandleAutoVacLauncherInterrupts(void); static void HandleAutoVacLauncherInterrupts(void);
static void AutoVacLauncherShutdown(void) pg_attribute_noreturn(); static void AutoVacLauncherShutdown(void) pg_attribute_noreturn();
@ -361,76 +353,23 @@ static void avl_sigusr2_handler(SIGNAL_ARGS);
* AUTOVACUUM LAUNCHER CODE * AUTOVACUUM LAUNCHER CODE
********************************************************************/ ********************************************************************/
#ifdef EXEC_BACKEND
/* /*
* forkexec routine for the autovacuum launcher process. * Main entry point for the autovacuum launcher process.
*
* Format up the arglist, then fork and exec.
*/ */
static pid_t void
avlauncher_forkexec(void) AutoVacLauncherMain(char *startup_data, size_t startup_data_len)
{
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);
}
#endif
/*
* Main entry point for autovacuum launcher process, to be called from the
* postmaster.
*/
int
StartAutoVacLauncher(void)
{
pid_t AutoVacPID;
#ifdef EXEC_BACKEND
switch ((AutoVacPID = avlauncher_forkexec()))
#else
switch ((AutoVacPID = fork_process()))
#endif
{
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;
}
/* 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; sigjmp_buf local_sigjmp_buf;
Assert(startup_data_len == 0);
/* Release postmaster's working memory context */
if (PostmasterContext)
{
MemoryContextDelete(PostmasterContext);
PostmasterContext = NULL;
}
MyBackendType = B_AUTOVAC_LAUNCHER; MyBackendType = B_AUTOVAC_LAUNCHER;
init_ps_display(NULL); init_ps_display(NULL);
@ -1412,78 +1351,24 @@ avl_sigusr2_handler(SIGNAL_ARGS)
* AUTOVACUUM WORKER CODE * AUTOVACUUM WORKER CODE
********************************************************************/ ********************************************************************/
#ifdef EXEC_BACKEND
/* /*
* forkexec routines for the autovacuum worker. * Main entry point for autovacuum worker processes.
*
* Format up the arglist, then fork and exec.
*/ */
static pid_t void
avworker_forkexec(void) AutoVacWorkerMain(char *startup_data, size_t startup_data_len)
{
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);
}
#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;
#ifdef EXEC_BACKEND
switch ((worker_pid = avworker_forkexec()))
#else
switch ((worker_pid = fork_process()))
#endif
{
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;
}
/* shouldn't get here */
return 0;
}
/*
* AutoVacWorkerMain
*/
NON_EXEC_STATIC void
AutoVacWorkerMain(int argc, char *argv[])
{ {
sigjmp_buf local_sigjmp_buf; sigjmp_buf local_sigjmp_buf;
Oid dbid; Oid dbid;
Assert(startup_data_len == 0);
/* Release postmaster's working memory context */
if (PostmasterContext)
{
MemoryContextDelete(PostmasterContext);
PostmasterContext = NULL;
}
MyBackendType = B_AUTOVAC_WORKER; MyBackendType = B_AUTOVAC_WORKER;
init_ps_display(NULL); init_ps_display(NULL);

View File

@ -27,6 +27,7 @@
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/proc.h" #include "storage/proc.h"
#include "storage/procsignal.h" #include "storage/procsignal.h"
#include "utils/memutils.h"
#include "utils/ps_status.h" #include "utils/ps_status.h"
@ -34,19 +35,22 @@ static void ShutdownAuxiliaryProcess(int code, Datum arg);
/* /*
* AuxiliaryProcessMain * AuxiliaryProcessMainCommon
* *
* 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. * walwriter, walreceiver, and the startup process.
*
* This code is here just because of historical reasons.
*/ */
void void
AuxiliaryProcessMain(BackendType auxtype) AuxiliaryProcessMainCommon(void)
{ {
Assert(IsUnderPostmaster); Assert(IsUnderPostmaster);
MyBackendType = auxtype; /* Release postmaster's working memory context */
if (PostmasterContext)
{
MemoryContextDelete(PostmasterContext);
PostmasterContext = NULL;
}
init_ps_display(NULL); init_ps_display(NULL);
@ -84,41 +88,6 @@ AuxiliaryProcessMain(BackendType auxtype)
before_shmem_exit(ShutdownAuxiliaryProcess, 0); before_shmem_exit(ShutdownAuxiliaryProcess, 0);
SetProcessingMode(NormalProcessing); SetProcessingMode(NormalProcessing);
switch (MyBackendType)
{
case B_STARTUP:
StartupProcessMain();
proc_exit(1);
case B_ARCHIVER:
PgArchiverMain();
proc_exit(1);
case B_BG_WRITER:
BackgroundWriterMain();
proc_exit(1);
case B_CHECKPOINTER:
CheckpointerMain();
proc_exit(1);
case B_WAL_WRITER:
WalWriterMain();
proc_exit(1);
case B_WAL_RECEIVER:
WalReceiverMain();
proc_exit(1);
case B_WAL_SUMMARIZER:
WalSummarizerMain();
proc_exit(1);
default:
elog(PANIC, "unrecognized process type: %d", (int) MyBackendType);
proc_exit(1);
}
} }
/* /*

View File

@ -720,15 +720,29 @@ bgworker_die(SIGNAL_ARGS)
* Main entry point for background worker processes. * Main entry point for background worker processes.
*/ */
void void
BackgroundWorkerMain(void) BackgroundWorkerMain(char *startup_data, size_t startup_data_len)
{ {
sigjmp_buf local_sigjmp_buf; sigjmp_buf local_sigjmp_buf;
BackgroundWorker *worker = MyBgworkerEntry; BackgroundWorker *worker;
bgworker_main_type entrypt; bgworker_main_type entrypt;
if (worker == NULL) if (startup_data == NULL)
elog(FATAL, "unable to find bgworker entry"); elog(FATAL, "unable to find bgworker entry");
Assert(startup_data_len == sizeof(BackgroundWorker));
worker = MemoryContextAlloc(TopMemoryContext, sizeof(BackgroundWorker));
memcpy(worker, startup_data, sizeof(BackgroundWorker));
/*
* Now that we're done reading the startup data, release postmaster's
* working memory context.
*/
if (PostmasterContext)
{
MemoryContextDelete(PostmasterContext);
PostmasterContext = NULL;
}
MyBgworkerEntry = worker;
MyBackendType = B_BG_WORKER; MyBackendType = B_BG_WORKER;
init_ps_display(worker->bgw_name); init_ps_display(worker->bgw_name);

View File

@ -35,6 +35,7 @@
#include "libpq/pqsignal.h" #include "libpq/pqsignal.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pgstat.h" #include "pgstat.h"
#include "postmaster/auxprocess.h"
#include "postmaster/bgwriter.h" #include "postmaster/bgwriter.h"
#include "postmaster/interrupt.h" #include "postmaster/interrupt.h"
#include "storage/buf_internals.h" #include "storage/buf_internals.h"
@ -83,13 +84,18 @@ static XLogRecPtr last_snapshot_lsn = InvalidXLogRecPtr;
* basic execution environment, but not enabled signals yet. * basic execution environment, but not enabled signals yet.
*/ */
void void
BackgroundWriterMain(void) BackgroundWriterMain(char *startup_data, size_t startup_data_len)
{ {
sigjmp_buf local_sigjmp_buf; sigjmp_buf local_sigjmp_buf;
MemoryContext bgwriter_context; MemoryContext bgwriter_context;
bool prev_hibernate; bool prev_hibernate;
WritebackContext wb_context; WritebackContext wb_context;
Assert(startup_data_len == 0);
MyBackendType = B_BG_WRITER;
AuxiliaryProcessMainCommon();
/* /*
* Properly accept or ignore signals that might be sent to us. * Properly accept or ignore signals that might be sent to us.
*/ */

View File

@ -42,6 +42,7 @@
#include "libpq/pqsignal.h" #include "libpq/pqsignal.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pgstat.h" #include "pgstat.h"
#include "postmaster/auxprocess.h"
#include "postmaster/bgwriter.h" #include "postmaster/bgwriter.h"
#include "postmaster/interrupt.h" #include "postmaster/interrupt.h"
#include "replication/syncrep.h" #include "replication/syncrep.h"
@ -169,11 +170,16 @@ static void ReqCheckpointHandler(SIGNAL_ARGS);
* basic execution environment, but not enabled signals yet. * basic execution environment, but not enabled signals yet.
*/ */
void void
CheckpointerMain(void) CheckpointerMain(char *startup_data, size_t startup_data_len)
{ {
sigjmp_buf local_sigjmp_buf; sigjmp_buf local_sigjmp_buf;
MemoryContext checkpointer_context; MemoryContext checkpointer_context;
Assert(startup_data_len == 0);
MyBackendType = B_CHECKPOINTER;
AuxiliaryProcessMainCommon();
CheckpointerShmem->checkpointer_pid = MyProcPid; CheckpointerShmem->checkpointer_pid = MyProcPid;
/* /*

View File

@ -49,7 +49,9 @@
#include "postmaster/postmaster.h" #include "postmaster/postmaster.h"
#include "postmaster/startup.h" #include "postmaster/startup.h"
#include "postmaster/syslogger.h" #include "postmaster/syslogger.h"
#include "postmaster/walsummarizer.h"
#include "postmaster/walwriter.h" #include "postmaster/walwriter.h"
#include "replication/slotsync.h"
#include "replication/walreceiver.h" #include "replication/walreceiver.h"
#include "storage/fd.h" #include "storage/fd.h"
#include "storage/ipc.h" #include "storage/ipc.h"
@ -89,13 +91,6 @@ typedef int InheritableSocket;
*/ */
typedef struct typedef struct
{ {
bool has_client_sock;
ClientSocket client_sock;
InheritableSocket inh_sock;
bool has_bgworker;
BackgroundWorker bgworker;
char DataDir[MAXPGPATH]; char DataDir[MAXPGPATH];
int32 MyCancelKey; int32 MyCancelKey;
int MyPMChildSlot; int MyPMChildSlot;
@ -138,22 +133,144 @@ typedef struct
#endif #endif
char my_exec_path[MAXPGPATH]; char my_exec_path[MAXPGPATH];
char pkglib_path[MAXPGPATH]; char pkglib_path[MAXPGPATH];
/*
* These are only used by backend processes, but are 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;
/*
* Extra startup data, content depends on the child process.
*/
size_t startup_data_len;
char startup_data[FLEXIBLE_ARRAY_MEMBER];
} BackendParameters; } BackendParameters;
#define SizeOfBackendParameters(startup_data_len) (offsetof(BackendParameters, startup_data) + startup_data_len) #define SizeOfBackendParameters(startup_data_len) (offsetof(BackendParameters, startup_data) + startup_data_len)
void read_backend_variables(char *id, ClientSocket **client_sock, BackgroundWorker **worker); static void read_backend_variables(char *id, char **startup_data, size_t *startup_data_len);
static void restore_backend_variables(BackendParameters *param, ClientSocket **client_sock, BackgroundWorker **worker); static void restore_backend_variables(BackendParameters *param);
#ifndef WIN32 static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock,
static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock, BackgroundWorker *worker); #ifdef WIN32
#else HANDLE childProcess, pid_t childPid,
static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock, BackgroundWorker *worker,
HANDLE childProcess, pid_t childPid);
#endif #endif
char *startup_data, size_t startup_data_len);
pid_t internal_forkexec(int argc, char *argv[], ClientSocket *client_sock, BackgroundWorker *worker); static pid_t internal_forkexec(const char *child_kind, char *startup_data, size_t startup_data_len, ClientSocket *client_sock);
#endif /* EXEC_BACKEND */
/*
* Information needed to launch different kinds of child processes.
*/
typedef struct
{
const char *name;
void (*main_fn) (char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
bool shmem_attach;
} child_process_kind;
child_process_kind child_process_kinds[] = {
[B_INVALID] = {"invalid", NULL, false},
[B_BACKEND] = {"backend", BackendMain, true},
[B_AUTOVAC_LAUNCHER] = {"autovacuum launcher", AutoVacLauncherMain, true},
[B_AUTOVAC_WORKER] = {"autovacuum worker", AutoVacWorkerMain, true},
[B_BG_WORKER] = {"bgworker", BackgroundWorkerMain, true},
/*
* WAL senders start their life as regular backend processes, and change
* their type after authenticating the client for replication. We list it
* here forPostmasterChildName() but cannot launch them directly.
*/
[B_WAL_SENDER] = {"wal sender", NULL, true},
[B_SLOTSYNC_WORKER] = {"slot sync worker", ReplSlotSyncWorkerMain, true},
[B_STANDALONE_BACKEND] = {"standalone backend", NULL, false},
[B_ARCHIVER] = {"archiver", PgArchiverMain, true},
[B_BG_WRITER] = {"bgwriter", BackgroundWriterMain, true},
[B_CHECKPOINTER] = {"checkpointer", CheckpointerMain, true},
[B_STARTUP] = {"startup", StartupProcessMain, true},
[B_WAL_RECEIVER] = {"wal_receiver", WalReceiverMain, true},
[B_WAL_SUMMARIZER] = {"wal_summarizer", WalSummarizerMain, true},
[B_WAL_WRITER] = {"wal_writer", WalWriterMain, true},
[B_LOGGER] = {"syslogger", SysLoggerMain, false},
};
const char *
PostmasterChildName(BackendType child_type)
{
Assert(child_type >= 0 && child_type < lengthof(child_process_kinds));
return child_process_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(BackendType child_type,
char *startup_data, size_t startup_data_len,
ClientSocket *client_sock)
{
pid_t pid;
Assert(child_type >= 0 && child_type < lengthof(child_process_kinds));
Assert(IsPostmasterEnvironment && !IsUnderPostmaster);
#ifdef EXEC_BACKEND
pid = internal_forkexec(child_process_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 */
{
/* Close the postmaster's sockets */
ClosePostmasterPorts(child_type == B_LOGGER);
/* Detangle from postmaster */
InitPostmasterChild();
/*
* Enter the Main function with TopMemoryContext. The startup data is
* allocated in PostmasterContext, so we cannot release it here yet.
* The Main function will do it after it's done handling the startup
* data.
*/
MemoryContextSwitchTo(TopMemoryContext);
if (client_sock)
{
MyClientSocket = palloc(sizeof(ClientSocket));
memcpy(MyClientSocket, client_sock, sizeof(ClientSocket));
}
/*
* Run the appropriate Main function
*/
child_process_kinds[child_type].main_fn(startup_data, startup_data_len);
pg_unreachable(); /* main_fn never returns */
}
#endif /* EXEC_BACKEND */
return pid;
}
#ifdef EXEC_BACKEND
#ifndef WIN32 #ifndef WIN32
/* /*
@ -162,25 +279,32 @@ pid_t internal_forkexec(int argc, char *argv[], ClientSocket *client_sock, Back
* - writes out backend variables to the parameter file * - writes out backend variables to the parameter file
* - fork():s, and then exec():s the child process * - fork():s, and then exec():s the child process
*/ */
pid_t static pid_t
internal_forkexec(int argc, char *argv[], ClientSocket *client_sock, BackgroundWorker *worker) internal_forkexec(const char *child_kind, char *startup_data, size_t startup_data_len, ClientSocket *client_sock)
{ {
static unsigned long tmpBackendFileNum = 0; static unsigned long tmpBackendFileNum = 0;
pid_t pid; pid_t pid;
char tmpfilename[MAXPGPATH]; char tmpfilename[MAXPGPATH];
BackendParameters param; size_t paramsz;
BackendParameters *param;
FILE *fp; FILE *fp;
char *argv[4];
char forkav[MAXPGPATH];
/* /*
* Make sure padding bytes are initialized, to prevent Valgrind from * Use palloc0 to make sure padding bytes are initialized, to prevent
* complaining about writing uninitialized bytes to the file. This isn't * Valgrind from complaining about writing uninitialized bytes to the
* performance critical, and the win32 implementation initializes the * file. This isn't performance critical, and the win32 implementation
* padding bytes to zeros, so do it even when not using Valgrind. * initializes the padding bytes to zeros, so do it even when not using
* Valgrind.
*/ */
memset(&param, 0, sizeof(BackendParameters)); paramsz = SizeOfBackendParameters(startup_data_len);
param = palloc0(paramsz);
if (!save_backend_variables(&param, client_sock, worker)) if (!save_backend_variables(param, client_sock, startup_data, startup_data_len))
{
pfree(param);
return -1; /* log made by save_backend_variables */ return -1; /* log made by save_backend_variables */
}
/* Calculate name for temp file */ /* Calculate name for temp file */
snprintf(tmpfilename, MAXPGPATH, "%s/%s.backend_var.%d.%lu", snprintf(tmpfilename, MAXPGPATH, "%s/%s.backend_var.%d.%lu",
@ -204,18 +328,21 @@ internal_forkexec(int argc, char *argv[], ClientSocket *client_sock, BackgroundW
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not create file \"%s\": %m", errmsg("could not create file \"%s\": %m",
tmpfilename))); tmpfilename)));
pfree(param);
return -1; return -1;
} }
} }
if (fwrite(&param, sizeof(param), 1, fp) != 1) if (fwrite(param, paramsz, 1, fp) != 1)
{ {
ereport(LOG, ereport(LOG,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not write to file \"%s\": %m", tmpfilename))); errmsg("could not write to file \"%s\": %m", tmpfilename)));
FreeFile(fp); FreeFile(fp);
pfree(param);
return -1; return -1;
} }
pfree(param);
/* Release file */ /* Release file */
if (FreeFile(fp)) if (FreeFile(fp))
@ -226,14 +353,13 @@ internal_forkexec(int argc, char *argv[], ClientSocket *client_sock, BackgroundW
return -1; return -1;
} }
/* Make sure caller set up argv properly */ /* set up argv properly */
Assert(argc >= 3); argv[0] = "postgres";
Assert(argv[argc] == NULL); snprintf(forkav, MAXPGPATH, "--forkchild=%s", child_kind);
Assert(strncmp(argv[1], "--fork", 6) == 0); argv[1] = forkav;
Assert(argv[2] == NULL); /* Insert temp file name after --forkchild argument */
/* Insert temp file name after --fork argument */
argv[2] = tmpfilename; argv[2] = tmpfilename;
argv[3] = NULL;
/* Fire off execv in child */ /* Fire off execv in child */
if ((pid = fork_process()) == 0) if ((pid = fork_process()) == 0)
@ -262,25 +388,21 @@ internal_forkexec(int argc, char *argv[], ClientSocket *client_sock, BackgroundW
* - resumes execution of the new process once the backend parameter * - resumes execution of the new process once the backend parameter
* file is complete. * file is complete.
*/ */
pid_t static pid_t
internal_forkexec(int argc, char *argv[], ClientSocket *client_sock, BackgroundWorker *worker) internal_forkexec(const char *child_kind, char *startup_data, size_t startup_data_len, ClientSocket *client_sock)
{ {
int retry_count = 0; int retry_count = 0;
STARTUPINFO si; STARTUPINFO si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
int i;
int j;
char cmdLine[MAXPGPATH * 2]; char cmdLine[MAXPGPATH * 2];
HANDLE paramHandle; HANDLE paramHandle;
BackendParameters *param; BackendParameters *param;
SECURITY_ATTRIBUTES sa; SECURITY_ATTRIBUTES sa;
size_t paramsz;
char paramHandleStr[32]; char paramHandleStr[32];
int l;
/* Make sure caller set up argv properly */ paramsz = SizeOfBackendParameters(startup_data_len);
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 */ /* Resume here if we need to retry */
retry: retry:
@ -293,7 +415,7 @@ retry:
&sa, &sa,
PAGE_READWRITE, PAGE_READWRITE,
0, 0,
sizeof(BackendParameters), paramsz,
NULL); NULL);
if (paramHandle == INVALID_HANDLE_VALUE) if (paramHandle == INVALID_HANDLE_VALUE)
{ {
@ -302,8 +424,7 @@ retry:
GetLastError()))); GetLastError())));
return -1; return -1;
} }
param = MapViewOfFile(paramHandle, FILE_MAP_WRITE, 0, 0, paramsz);
param = MapViewOfFile(paramHandle, FILE_MAP_WRITE, 0, 0, sizeof(BackendParameters));
if (!param) if (!param)
{ {
ereport(LOG, ereport(LOG,
@ -313,25 +434,15 @@ retry:
return -1; return -1;
} }
/* Insert temp file name after --fork argument */ /* Format the cmd line */
#ifdef _WIN64 #ifdef _WIN64
sprintf(paramHandleStr, "%llu", (LONG_PTR) paramHandle); sprintf(paramHandleStr, "%llu", (LONG_PTR) paramHandle);
#else #else
sprintf(paramHandleStr, "%lu", (DWORD) paramHandle); sprintf(paramHandleStr, "%lu", (DWORD) paramHandle);
#endif #endif
argv[2] = paramHandleStr; l = snprintf(cmdLine, sizeof(cmdLine) - 1, "\"%s\" --forkchild=\"%s\" %s",
postgres_exec_path, child_kind, paramHandleStr);
/* Format the cmd line */ if (l >= sizeof(cmdLine))
cmdLine[sizeof(cmdLine) - 1] = '\0';
cmdLine[sizeof(cmdLine) - 2] = '\0';
snprintf(cmdLine, sizeof(cmdLine) - 1, "\"%s\"", postgres_exec_path);
i = 0;
while (argv[++i] != NULL)
{
j = strlen(cmdLine);
snprintf(cmdLine + j, sizeof(cmdLine) - 1 - j, " \"%s\"", argv[i]);
}
if (cmdLine[sizeof(cmdLine) - 2] != '\0')
{ {
ereport(LOG, ereport(LOG,
(errmsg("subprocess command line too long"))); (errmsg("subprocess command line too long")));
@ -359,7 +470,7 @@ retry:
return -1; return -1;
} }
if (!save_backend_variables(param, client_sock, worker, pi.hProcess, pi.dwProcessId)) 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 * log made by save_backend_variables, but we have to clean up the
@ -445,6 +556,119 @@ retry:
} }
#endif /* WIN32 */ #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[])
{
char *startup_data;
size_t startup_data_len;
char *child_kind;
BackendType child_type;
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");
/* Find the entry in child_process_kinds */
if (strncmp(argv[1], "--forkchild=", 12) != 0)
elog(FATAL, "invalid subpostmaster invocation (--forkchild argument missing)");
child_kind = argv[1] + 12;
found = false;
for (int idx = 0; idx < lengthof(child_process_kinds); idx++)
{
if (strcmp(child_process_kinds[idx].name, child_kind) == 0)
{
child_type = (BackendType) idx;
found = true;
break;
}
}
if (!found)
elog(ERROR, "unknown child kind %s", child_kind);
/* Read in the variables file */
read_backend_variables(argv[2], &startup_data, &startup_data_len);
/* Close the postmaster's sockets (as soon as we know them) */
ClosePostmasterPorts(child_type == B_LOGGER);
/* 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 (child_process_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 the appropriate Main function
*/
child_process_kinds[child_type].main_fn(startup_data, startup_data_len);
pg_unreachable(); /* main_fn never returns */
}
/* /*
* The following need to be available to the save/restore_backend_variables * The following need to be available to the save/restore_backend_variables
* functions. They are marked NON_EXEC_STATIC in their home modules. * functions. They are marked NON_EXEC_STATIC in their home modules.
@ -469,38 +693,21 @@ static void read_inheritable_socket(SOCKET *dest, InheritableSocket *src);
/* Save critical backend variables into the BackendParameters struct */ /* Save critical backend variables into the BackendParameters struct */
#ifndef WIN32
static bool static bool
save_backend_variables(BackendParameters *param, ClientSocket *client_sock, BackgroundWorker *worker) save_backend_variables(BackendParameters *param, ClientSocket *client_sock,
#else #ifdef WIN32
static bool HANDLE childProcess, pid_t childPid,
save_backend_variables(BackendParameters *param, ClientSocket *client_sock, BackgroundWorker *worker,
HANDLE childProcess, pid_t childPid)
#endif #endif
char *startup_data, size_t startup_data_len)
{ {
if (client_sock) if (client_sock)
{
memcpy(&param->client_sock, client_sock, sizeof(ClientSocket)); memcpy(&param->client_sock, client_sock, sizeof(ClientSocket));
if (!write_inheritable_socket(&param->inh_sock, client_sock->sock, childPid))
return false;
param->has_client_sock = true;
}
else else
{
memset(&param->client_sock, 0, sizeof(ClientSocket)); memset(&param->client_sock, 0, sizeof(ClientSocket));
param->has_client_sock = false; if (!write_inheritable_socket(&param->inh_sock,
} client_sock ? client_sock->sock : PGINVALID_SOCKET,
childPid))
if (worker) return false;
{
memcpy(&param->bgworker, worker, sizeof(BackgroundWorker));
param->has_bgworker = true;
}
else
{
memset(&param->bgworker, 0, sizeof(BackgroundWorker));
param->has_bgworker = false;
}
strlcpy(param->DataDir, DataDir, MAXPGPATH); strlcpy(param->DataDir, DataDir, MAXPGPATH);
@ -557,6 +764,9 @@ save_backend_variables(BackendParameters *param, ClientSocket *client_sock, Back
strlcpy(param->pkglib_path, pkglib_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; return true;
} }
@ -653,8 +863,8 @@ read_inheritable_socket(SOCKET *dest, InheritableSocket *src)
} }
#endif #endif
void static void
read_backend_variables(char *id, ClientSocket **client_sock, BackgroundWorker **worker) read_backend_variables(char *id, char **startup_data, size_t *startup_data_len)
{ {
BackendParameters param; BackendParameters param;
@ -676,6 +886,21 @@ read_backend_variables(char *id, ClientSocket **client_sock, BackgroundWorker **
exit(1); 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\": %m\n",
id);
exit(1);
}
}
else
*startup_data = NULL;
/* Release file */ /* Release file */
FreeFile(fp); FreeFile(fp);
if (unlink(id) != 0) if (unlink(id) != 0)
@ -703,6 +928,16 @@ read_backend_variables(char *id, ClientSocket **client_sock, BackgroundWorker **
memcpy(&param, paramp, sizeof(BackendParameters)); memcpy(&param, paramp, sizeof(BackendParameters));
/* read startup data */
*startup_data_len = param.startup_data_len;
if (param.startup_data_len > 0)
{
*startup_data = palloc(paramp->startup_data_len);
memcpy(*startup_data, paramp->startup_data, param.startup_data_len);
}
else
*startup_data = NULL;
if (!UnmapViewOfFile(paramp)) if (!UnmapViewOfFile(paramp))
{ {
write_stderr("could not unmap view of backend variables: error code %lu\n", write_stderr("could not unmap view of backend variables: error code %lu\n",
@ -718,30 +953,19 @@ read_backend_variables(char *id, ClientSocket **client_sock, BackgroundWorker **
} }
#endif #endif
restore_backend_variables(&param, client_sock, worker); restore_backend_variables(&param);
} }
/* Restore critical backend variables from the BackendParameters struct */ /* Restore critical backend variables from the BackendParameters struct */
static void static void
restore_backend_variables(BackendParameters *param, ClientSocket **client_sock, BackgroundWorker **worker) restore_backend_variables(BackendParameters *param)
{ {
if (param->has_client_sock) if (param->client_sock.sock != PGINVALID_SOCKET)
{ {
*client_sock = (ClientSocket *) MemoryContextAlloc(TopMemoryContext, sizeof(ClientSocket)); MyClientSocket = MemoryContextAlloc(TopMemoryContext, sizeof(ClientSocket));
memcpy(*client_sock, &param->client_sock, sizeof(ClientSocket)); memcpy(MyClientSocket, &param->client_sock, sizeof(ClientSocket));
read_inheritable_socket(&(*client_sock)->sock, &param->inh_sock); read_inheritable_socket(&MyClientSocket->sock, &param->inh_sock);
} }
else
*client_sock = NULL;
if (param->has_bgworker)
{
*worker = (BackgroundWorker *)
MemoryContextAlloc(TopMemoryContext, sizeof(BackgroundWorker));
memcpy(*worker, &param->bgworker, sizeof(BackgroundWorker));
}
else
*worker = NULL;
SetDataDir(param->DataDir); SetDataDir(param->DataDir);

View File

@ -36,6 +36,7 @@
#include "lib/binaryheap.h" #include "lib/binaryheap.h"
#include "libpq/pqsignal.h" #include "libpq/pqsignal.h"
#include "pgstat.h" #include "pgstat.h"
#include "postmaster/auxprocess.h"
#include "postmaster/interrupt.h" #include "postmaster/interrupt.h"
#include "postmaster/pgarch.h" #include "postmaster/pgarch.h"
#include "storage/fd.h" #include "storage/fd.h"
@ -209,8 +210,13 @@ PgArchCanRestart(void)
/* Main entry point for archiver process */ /* Main entry point for archiver process */
void void
PgArchiverMain(void) PgArchiverMain(char *startup_data, size_t startup_data_len)
{ {
Assert(startup_data_len == 0);
MyBackendType = B_ARCHIVER;
AuxiliaryProcessMainCommon();
/* /*
* Ignore all signals usually bound to some action in the postmaster, * Ignore all signals usually bound to some action in the postmaster,
* except for SIGHUP, SIGTERM, SIGUSR1, SIGUSR2, and SIGQUIT. * except for SIGHUP, SIGTERM, SIGUSR1, SIGUSR2, and SIGQUIT.

View File

@ -2,9 +2,9 @@
* *
* postmaster.c * postmaster.c
* This program acts as a clearing house for requests to the * This program acts as a clearing house for requests to the
* POSTGRES system. Frontend programs send a startup message * POSTGRES system. Frontend programs connect to the Postmaster,
* to the Postmaster and the postmaster uses the info in the * and postmaster forks a new backend process to handle the
* message to setup a backend process. * connection.
* *
* The postmaster also manages system-wide operations such as * The postmaster also manages system-wide operations such as
* startup and shutdown. The postmaster itself doesn't do those * startup and shutdown. The postmaster itself doesn't do those
@ -106,7 +106,6 @@
#include "postmaster/autovacuum.h" #include "postmaster/autovacuum.h"
#include "postmaster/auxprocess.h" #include "postmaster/auxprocess.h"
#include "postmaster/bgworker_internals.h" #include "postmaster/bgworker_internals.h"
#include "postmaster/fork_process.h"
#include "postmaster/pgarch.h" #include "postmaster/pgarch.h"
#include "postmaster/postmaster.h" #include "postmaster/postmaster.h"
#include "postmaster/syslogger.h" #include "postmaster/syslogger.h"
@ -427,7 +426,6 @@ typedef enum CAC_state
} CAC_state; } CAC_state;
static void BackendInitialize(ClientSocket *client_sock, CAC_state cac); 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 void ExitPostmaster(int status) pg_attribute_noreturn();
static int ServerLoop(void); static int ServerLoop(void);
static int BackendStartup(ClientSocket *client_sock); static int BackendStartup(ClientSocket *client_sock);
@ -485,13 +483,6 @@ typedef struct
} win32_deadchild_waitinfo; } win32_deadchild_waitinfo;
#endif /* WIN32 */ #endif /* WIN32 */
static pid_t backend_forkexec(ClientSocket *client_sock, CAC_state cac);
/* in launch_backend.c */
extern pid_t internal_forkexec(int argc, char *argv[], ClientSocket *client_sock, BackgroundWorker *worker);
extern void read_backend_variables(char *id, ClientSocket **client_sock, BackgroundWorker **worker);
static void ShmemBackendArrayAdd(Backend *bn); static void ShmemBackendArrayAdd(Backend *bn);
static void ShmemBackendArrayRemove(Backend *bn); static void ShmemBackendArrayRemove(Backend *bn);
#endif /* EXEC_BACKEND */ #endif /* EXEC_BACKEND */
@ -1748,7 +1739,7 @@ ServerLoop(void)
(AutoVacuumingActive() || start_autovac_launcher) && (AutoVacuumingActive() || start_autovac_launcher) &&
pmState == PM_RUN) pmState == PM_RUN)
{ {
AutoVacPID = StartAutoVacLauncher(); AutoVacPID = StartChildProcess(B_AUTOVAC_LAUNCHER);
if (AutoVacPID != 0) if (AutoVacPID != 0)
start_autovac_launcher = false; /* signal processed */ start_autovac_launcher = false; /* signal processed */
} }
@ -2902,7 +2893,7 @@ process_pm_child_exit(void)
* situation, some of them may be alive already. * situation, some of them may be alive already.
*/ */
if (!IsBinaryUpgrade && AutoVacuumingActive() && AutoVacPID == 0) if (!IsBinaryUpgrade && AutoVacuumingActive() && AutoVacPID == 0)
AutoVacPID = StartAutoVacLauncher(); AutoVacPID = StartChildProcess(B_AUTOVAC_LAUNCHER);
if (PgArchStartupAllowed() && PgArchPID == 0) if (PgArchStartupAllowed() && PgArchPID == 0)
PgArchPID = StartChildProcess(B_ARCHIVER); PgArchPID = StartChildProcess(B_ARCHIVER);
MaybeStartSlotSyncWorker(); MaybeStartSlotSyncWorker();
@ -3964,6 +3955,12 @@ TerminateChildren(int signal)
signal_child(SlotSyncWorkerPID, signal); signal_child(SlotSyncWorkerPID, signal);
} }
/* Information passed from postmaster to backend process */
typedef struct BackendStartupData
{
CAC_state canAcceptConnections;
} BackendStartupData;
/* /*
* BackendStartup -- start backend process * BackendStartup -- start backend process
* *
@ -3976,7 +3973,7 @@ BackendStartup(ClientSocket *client_sock)
{ {
Backend *bn; /* for backend cleanup */ Backend *bn; /* for backend cleanup */
pid_t pid; pid_t pid;
CAC_state cac; BackendStartupData startup_data;
/* /*
* Create backend data structure. Better before the fork() so we can * Create backend data structure. Better before the fork() so we can
@ -4005,11 +4002,10 @@ BackendStartup(ClientSocket *client_sock)
return STATUS_ERROR; return STATUS_ERROR;
} }
bn->cancel_key = MyCancelKey;
/* Pass down canAcceptConnections state */ /* Pass down canAcceptConnections state */
cac = canAcceptConnections(BACKEND_TYPE_NORMAL); startup_data.canAcceptConnections = canAcceptConnections(BACKEND_TYPE_NORMAL);
bn->dead_end = (cac != CAC_OK); bn->dead_end = (startup_data.canAcceptConnections != CAC_OK);
bn->cancel_key = MyCancelKey;
/* /*
* Unless it's a dead_end child, assign it a child slot number * Unless it's a dead_end child, assign it a child slot number
@ -4022,26 +4018,9 @@ BackendStartup(ClientSocket *client_sock)
/* Hasn't asked to be notified about any bgworkers yet */ /* Hasn't asked to be notified about any bgworkers yet */
bn->bgworker_notify = false; bn->bgworker_notify = false;
#ifdef EXEC_BACKEND pid = postmaster_child_launch(B_BACKEND,
pid = backend_forkexec(client_sock, cac); (char *) &startup_data, sizeof(startup_data),
#else /* !EXEC_BACKEND */ client_sock);
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 */
if (pid < 0) if (pid < 0)
{ {
/* in parent, fork failed */ /* in parent, fork failed */
@ -4351,16 +4330,43 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac)
set_ps_display("initializing"); set_ps_display("initializing");
} }
void
/* BackendMain(char *startup_data, size_t startup_data_len)
* BackendRun -- set up the backend's argument list and invoke PostgresMain()
*
* returns:
* Doesn't return at all.
*/
static void
BackendRun(void)
{ {
BackendStartupData *bsdata = (BackendStartupData *) startup_data;
Assert(startup_data_len == sizeof(BackendStartupData));
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, bsdata->canAcceptConnections);
/* /*
* Create a per-backend PGPROC struct in shared memory. We must do this * Create a per-backend PGPROC struct in shared memory. We must do this
* before we can use LWLocks or access any shared memory. * before we can use LWLocks or access any shared memory.
@ -4377,245 +4383,6 @@ BackendRun(void)
} }
#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[])
{
return internal_forkexec(argc, argv, NULL, NULL);
}
/*
* 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, NULL);
}
/*
* 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;
BackgroundWorker *worker;
/* 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 */
read_backend_variables(argv[2], &client_sock, &worker);
/* 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], "--forkssworker") == 0 ||
strcmp(argv[1], "--forkavworker") == 0 ||
strcmp(argv[1], "--forkaux") == 0 ||
strcmp(argv[1], "--forkbgworker") == 0)
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();
/* 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)
{
BackendType 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 (strcmp(argv[1], "--forkssworker") == 0)
{
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
ReplSlotSyncWorkerMain(argc - 2, argv + 2); /* does not return */
}
if (strcmp(argv[1], "--forkbgworker") == 0)
{
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
MyBgworkerEntry = worker;
BackgroundWorkerMain();
}
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 * ExitPostmaster -- cleanup
* *
@ -4912,87 +4679,12 @@ StartChildProcess(BackendType type)
{ {
pid_t pid; pid_t pid;
#ifdef EXEC_BACKEND pid = postmaster_child_launch(type, NULL, 0, NULL);
{
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 */
if (pid < 0) if (pid < 0)
{ {
/* in parent, fork failed */ /* in parent, fork failed */
int save_errno = errno; ereport(LOG,
(errmsg("could not fork \"%s\" process: %m", PostmasterChildName(type))));
errno = save_errno;
switch (type)
{
case B_STARTUP:
ereport(LOG,
(errmsg("could not fork startup process: %m")));
break;
case B_ARCHIVER:
ereport(LOG,
(errmsg("could not fork archiver process: %m")));
break;
case B_BG_WRITER:
ereport(LOG,
(errmsg("could not fork background writer process: %m")));
break;
case B_CHECKPOINTER:
ereport(LOG,
(errmsg("could not fork checkpointer process: %m")));
break;
case B_WAL_WRITER:
ereport(LOG,
(errmsg("could not fork WAL writer process: %m")));
break;
case B_WAL_RECEIVER:
ereport(LOG,
(errmsg("could not fork WAL receiver process: %m")));
break;
case B_WAL_SUMMARIZER:
ereport(LOG,
(errmsg("could not fork WAL summarizer process: %m")));
break;
default:
ereport(LOG,
(errmsg("could not fork process: %m")));
break;
}
/* /*
* fork failure is fatal during startup, but there's no need to choke * fork failure is fatal during startup, but there's no need to choke
@ -5056,7 +4748,7 @@ StartAutovacuumWorker(void)
bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot(); bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
bn->bgworker_notify = false; bn->bgworker_notify = false;
bn->pid = StartAutoVacWorker(); bn->pid = StartChildProcess(B_AUTOVAC_WORKER);
if (bn->pid > 0) if (bn->pid > 0)
{ {
bn->bkend_type = BACKEND_TYPE_AUTOVAC; bn->bkend_type = BACKEND_TYPE_AUTOVAC;
@ -5070,7 +4762,7 @@ StartAutovacuumWorker(void)
/* /*
* fork failed, fall through to report -- actual error message was * fork failed, fall through to report -- actual error message was
* logged by StartAutoVacWorker * logged by StartChildProcess
*/ */
(void) ReleasePostmasterChildSlot(bn->child_slot); (void) ReleasePostmasterChildSlot(bn->child_slot);
pfree(bn); pfree(bn);
@ -5153,7 +4845,7 @@ MaybeStartSlotSyncWorker(void)
if (SlotSyncWorkerPID == 0 && pmState == PM_HOT_STANDBY && if (SlotSyncWorkerPID == 0 && pmState == PM_HOT_STANDBY &&
Shutdown <= SmartShutdown && sync_replication_slots && Shutdown <= SmartShutdown && sync_replication_slots &&
ValidateSlotSyncParams(LOG) && SlotSyncWorkerCanRestart()) ValidateSlotSyncParams(LOG) && SlotSyncWorkerCanRestart())
SlotSyncWorkerPID = StartSlotSyncWorker(); SlotSyncWorkerPID = StartChildProcess(B_SLOTSYNC_WORKER);
} }
/* /*
@ -5293,24 +4985,6 @@ BackgroundWorkerUnblockSignals(void)
sigprocmask(SIG_SETMASK, &UnBlockSig, NULL); sigprocmask(SIG_SETMASK, &UnBlockSig, NULL);
} }
#ifdef EXEC_BACKEND
static pid_t
bgworker_forkexec(BackgroundWorker *worker)
{
char *av[10];
int ac = 0;
av[ac++] = "postgres";
av[ac++] = "--forkbgworker";
av[ac++] = NULL; /* filled in by internal_forkexec */
av[ac] = NULL;
Assert(ac < lengthof(av));
return internal_forkexec(ac, av, NULL, worker);
}
#endif
/* /*
* Start a new bgworker. * Start a new bgworker.
* Starting time conditions must have been checked already. * Starting time conditions must have been checked already.
@ -5347,65 +5021,32 @@ do_start_bgworker(RegisteredBgWorker *rw)
(errmsg_internal("starting background worker process \"%s\"", (errmsg_internal("starting background worker process \"%s\"",
rw->rw_worker.bgw_name))); rw->rw_worker.bgw_name)));
#ifdef EXEC_BACKEND worker_pid = postmaster_child_launch(B_BG_WORKER, (char *) &rw->rw_worker, sizeof(BackgroundWorker), NULL);
switch ((worker_pid = bgworker_forkexec(&rw->rw_worker))) if (worker_pid == -1)
#else
switch ((worker_pid = fork_process()))
#endif
{ {
case -1: /* in postmaster, fork failed ... */
/* in postmaster, fork failed ... */ ereport(LOG,
ereport(LOG, (errmsg("could not fork background worker process: %m")));
(errmsg("could not fork background worker process: %m"))); /* undo what assign_backendlist_entry did */
/* undo what assign_backendlist_entry did */ ReleasePostmasterChildSlot(rw->rw_child_slot);
ReleasePostmasterChildSlot(rw->rw_child_slot); rw->rw_child_slot = 0;
rw->rw_child_slot = 0; pfree(rw->rw_backend);
pfree(rw->rw_backend); rw->rw_backend = NULL;
rw->rw_backend = NULL; /* mark entry as crashed, so we'll try again later */
/* mark entry as crashed, so we'll try again later */ rw->rw_crashed_at = GetCurrentTimestamp();
rw->rw_crashed_at = GetCurrentTimestamp(); return false;
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();
exit(1); /* should not get here */
break;
#endif
default:
/* in postmaster, fork successful ... */
rw->rw_pid = worker_pid;
rw->rw_backend->pid = rw->rw_pid;
ReportBackgroundWorkerPID(rw);
/* add new worker to lists of backends */
dlist_push_head(&BackendList, &rw->rw_backend->elem);
#ifdef EXEC_BACKEND
ShmemBackendArrayAdd(rw->rw_backend);
#endif
return true;
} }
return false; /* in postmaster, fork successful ... */
rw->rw_pid = worker_pid;
rw->rw_backend->pid = rw->rw_pid;
ReportBackgroundWorkerPID(rw);
/* add new worker to lists of backends */
dlist_push_head(&BackendList, &rw->rw_backend->elem);
#ifdef EXEC_BACKEND
ShmemBackendArrayAdd(rw->rw_backend);
#endif
return true;
} }
/* /*

View File

@ -24,6 +24,7 @@
#include "access/xlogutils.h" #include "access/xlogutils.h"
#include "libpq/pqsignal.h" #include "libpq/pqsignal.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "postmaster/auxprocess.h"
#include "postmaster/startup.h" #include "postmaster/startup.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/pmsignal.h" #include "storage/pmsignal.h"
@ -212,8 +213,13 @@ StartupProcExit(int code, Datum arg)
* ---------------------------------- * ----------------------------------
*/ */
void void
StartupProcessMain(void) StartupProcessMain(char *startup_data, size_t startup_data_len)
{ {
Assert(startup_data_len == 0);
MyBackendType = B_STARTUP;
AuxiliaryProcessMainCommon();
/* Arrange to clean up at startup process exit */ /* Arrange to clean up at startup process exit */
on_shmem_exit(StartupProcExit, 0); on_shmem_exit(StartupProcExit, 0);

View File

@ -39,7 +39,6 @@
#include "pgstat.h" #include "pgstat.h"
#include "pgtime.h" #include "pgtime.h"
#include "port/pg_bitutils.h" #include "port/pg_bitutils.h"
#include "postmaster/fork_process.h"
#include "postmaster/interrupt.h" #include "postmaster/interrupt.h"
#include "postmaster/postmaster.h" #include "postmaster/postmaster.h"
#include "postmaster/syslogger.h" #include "postmaster/syslogger.h"
@ -50,6 +49,7 @@
#include "storage/pg_shmem.h" #include "storage/pg_shmem.h"
#include "tcop/tcopprot.h" #include "tcop/tcopprot.h"
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/ps_status.h" #include "utils/ps_status.h"
/* /*
@ -133,10 +133,7 @@ static volatile sig_atomic_t rotation_requested = false;
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
static int syslogger_fdget(FILE *file); static int syslogger_fdget(FILE *file);
static FILE *syslogger_fdopen(int fd); static FILE *syslogger_fdopen(int fd);
static pid_t syslogger_forkexec(void);
static void syslogger_parseArgs(int argc, char *argv[]);
#endif #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 process_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
static void flush_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, static FILE *logfile_open(const char *filename, const char *mode,
@ -155,13 +152,19 @@ static void set_next_rotation_time(void);
static void sigUsr1Handler(SIGNAL_ARGS); static void sigUsr1Handler(SIGNAL_ARGS);
static void update_metainfo_datafile(void); static void update_metainfo_datafile(void);
typedef struct
{
int syslogFile;
int csvlogFile;
int jsonlogFile;
} SysloggerStartupData;
/* /*
* Main entry point for syslogger process * Main entry point for syslogger process
* argc/argv parameters are valid only in EXEC_BACKEND case. * argc/argv parameters are valid only in EXEC_BACKEND case.
*/ */
NON_EXEC_STATIC void void
SysLoggerMain(int argc, char *argv[]) SysLoggerMain(char *startup_data, size_t startup_data_len)
{ {
#ifndef WIN32 #ifndef WIN32
char logbuffer[READ_BUF_SIZE]; char logbuffer[READ_BUF_SIZE];
@ -173,11 +176,37 @@ SysLoggerMain(int argc, char *argv[])
pg_time_t now; pg_time_t now;
WaitEventSet *wes; WaitEventSet *wes;
now = MyStartTime; /*
* 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 #ifdef EXEC_BACKEND
syslogger_parseArgs(argc, argv); {
#endif /* EXEC_BACKEND */ SysloggerStartupData *slsdata = (SysloggerStartupData *) startup_data;
Assert(startup_data_len == sizeof(*slsdata));
syslogFile = syslogger_fdopen(slsdata->syslogFile);
csvlogFile = syslogger_fdopen(slsdata->csvlogFile);
jsonlogFile = syslogger_fdopen(slsdata->jsonlogFile);
}
#else
Assert(startup_data_len == 0);
#endif
/*
* Now that we're done reading the startup data, release postmaster's
* working memory context.
*/
if (PostmasterContext)
{
MemoryContextDelete(PostmasterContext);
PostmasterContext = NULL;
}
now = MyStartTime;
MyBackendType = B_LOGGER; MyBackendType = B_LOGGER;
init_ps_display(NULL); init_ps_display(NULL);
@ -567,6 +596,9 @@ SysLogger_Start(void)
{ {
pid_t sysloggerPid; pid_t sysloggerPid;
char *filename; char *filename;
#ifdef EXEC_BACKEND
SysloggerStartupData startup_data;
#endif /* EXEC_BACKEND */
if (!Logging_collector) if (!Logging_collector)
return 0; return 0;
@ -666,112 +698,95 @@ SysLogger_Start(void)
} }
#ifdef EXEC_BACKEND #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(B_LOGGER, (char *) &startup_data, sizeof(startup_data), NULL);
#else #else
switch ((sysloggerPid = fork_process())) sysloggerPid = postmaster_child_launch(B_LOGGER, NULL, 0, NULL);
#endif #endif /* EXEC_BACKEND */
if (sysloggerPid == -1)
{ {
case -1: ereport(LOG,
ereport(LOG, (errmsg("could not fork system logger: %m")));
(errmsg("could not fork system logger: %m"))); return 0;
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();
/* do the work */
SysLoggerMain(0, NULL);
break;
#endif
default:
/* success, in postmaster */
/* now we redirect stderr, if not done already */
if (!redirection_done)
{
#ifdef WIN32
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)));
#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;
#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);
/*
* 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;
} }
/* we should never reach here */ /* success, in postmaster */
return 0;
/* now we redirect stderr, if not done already */
if (!redirection_done)
{
#ifdef WIN32
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)));
#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;
#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);
/*
* 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;
} }
@ -830,69 +845,6 @@ syslogger_fdopen(int fd)
return file; 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 */ #endif /* EXEC_BACKEND */

View File

@ -33,6 +33,7 @@
#include "common/blkreftable.h" #include "common/blkreftable.h"
#include "libpq/pqsignal.h" #include "libpq/pqsignal.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "postmaster/auxprocess.h"
#include "postmaster/interrupt.h" #include "postmaster/interrupt.h"
#include "postmaster/walsummarizer.h" #include "postmaster/walsummarizer.h"
#include "replication/walreceiver.h" #include "replication/walreceiver.h"
@ -206,7 +207,7 @@ WalSummarizerShmemInit(void)
* Entry point for walsummarizer process. * Entry point for walsummarizer process.
*/ */
void void
WalSummarizerMain(void) WalSummarizerMain(char *startup_data, size_t startup_data_len)
{ {
sigjmp_buf local_sigjmp_buf; sigjmp_buf local_sigjmp_buf;
MemoryContext context; MemoryContext context;
@ -228,6 +229,11 @@ WalSummarizerMain(void)
XLogRecPtr switch_lsn = InvalidXLogRecPtr; XLogRecPtr switch_lsn = InvalidXLogRecPtr;
TimeLineID switch_tli = 0; TimeLineID switch_tli = 0;
Assert(startup_data_len == 0);
MyBackendType = B_WAL_SUMMARIZER;
AuxiliaryProcessMainCommon();
ereport(DEBUG1, ereport(DEBUG1,
(errmsg_internal("WAL summarizer started"))); (errmsg_internal("WAL summarizer started")));

View File

@ -48,6 +48,7 @@
#include "libpq/pqsignal.h" #include "libpq/pqsignal.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pgstat.h" #include "pgstat.h"
#include "postmaster/auxprocess.h"
#include "postmaster/interrupt.h" #include "postmaster/interrupt.h"
#include "postmaster/walwriter.h" #include "postmaster/walwriter.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
@ -85,13 +86,18 @@ int WalWriterFlushAfter = DEFAULT_WAL_WRITER_FLUSH_AFTER;
* basic execution environment, but not enabled signals yet. * basic execution environment, but not enabled signals yet.
*/ */
void void
WalWriterMain(void) WalWriterMain(char *startup_data, size_t startup_data_len)
{ {
sigjmp_buf local_sigjmp_buf; sigjmp_buf local_sigjmp_buf;
MemoryContext walwriter_context; MemoryContext walwriter_context;
int left_till_hibernate; int left_till_hibernate;
bool hibernating; bool hibernating;
Assert(startup_data_len == 0);
MyBackendType = B_WAL_WRITER;
AuxiliaryProcessMainCommon();
/* /*
* Properly accept or ignore signals the postmaster might send us * Properly accept or ignore signals the postmaster might send us
* *

View File

@ -139,11 +139,6 @@ typedef struct RemoteSlot
ReplicationSlotInvalidationCause invalidated; ReplicationSlotInvalidationCause invalidated;
} RemoteSlot; } RemoteSlot;
#ifdef EXEC_BACKEND
static pid_t slotsyncworker_forkexec(void);
#endif
NON_EXEC_STATIC void ReplSlotSyncWorkerMain(int argc, char *argv[]) pg_attribute_noreturn();
static void slotsync_failure_callback(int code, Datum arg); static void slotsync_failure_callback(int code, Datum arg);
/* /*
@ -1113,8 +1108,8 @@ wait_for_slot_activity(bool some_slot_updated)
* It connects to the primary server, fetches logical failover slots * It connects to the primary server, fetches logical failover slots
* information periodically in order to create and sync the slots. * information periodically in order to create and sync the slots.
*/ */
NON_EXEC_STATIC void void
ReplSlotSyncWorkerMain(int argc, char *argv[]) ReplSlotSyncWorkerMain(char *startup_data, size_t startup_data_len)
{ {
WalReceiverConn *wrconn = NULL; WalReceiverConn *wrconn = NULL;
char *dbname; char *dbname;
@ -1122,6 +1117,8 @@ ReplSlotSyncWorkerMain(int argc, char *argv[])
sigjmp_buf local_sigjmp_buf; sigjmp_buf local_sigjmp_buf;
StringInfoData app_name; StringInfoData app_name;
Assert(startup_data_len == 0);
MyBackendType = B_SLOTSYNC_WORKER; MyBackendType = B_SLOTSYNC_WORKER;
init_ps_display(NULL); init_ps_display(NULL);
@ -1299,67 +1296,6 @@ ReplSlotSyncWorkerMain(int argc, char *argv[])
Assert(false); Assert(false);
} }
/*
* Main entry point for slot sync worker process, to be called from the
* postmaster.
*/
int
StartSlotSyncWorker(void)
{
pid_t pid;
#ifdef EXEC_BACKEND
switch ((pid = slotsyncworker_forkexec()))
{
#else
switch ((pid = fork_process()))
{
case 0:
/* in postmaster child ... */
InitPostmasterChild();
/* Close the postmaster's sockets */
ClosePostmasterPorts(false);
ReplSlotSyncWorkerMain(0, NULL);
break;
#endif
case -1:
ereport(LOG,
(errmsg("could not fork slot sync worker process: %m")));
return 0;
default:
return (int) pid;
}
/* shouldn't get here */
return 0;
}
#ifdef EXEC_BACKEND
/*
* The forkexec routine for the slot sync worker process.
*
* Format up the arglist, then fork and exec.
*/
static pid_t
slotsyncworker_forkexec(void)
{
char *av[10];
int ac = 0;
av[ac++] = "postgres";
av[ac++] = "--forkssworker";
av[ac++] = NULL; /* filled in by postmaster_forkexec */
av[ac] = NULL;
Assert(ac < lengthof(av));
return postmaster_forkexec(ac, av);
}
#endif
/* /*
* Shut down the slot sync worker. * Shut down the slot sync worker.
*/ */

View File

@ -63,6 +63,7 @@
#include "libpq/pqsignal.h" #include "libpq/pqsignal.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pgstat.h" #include "pgstat.h"
#include "postmaster/auxprocess.h"
#include "postmaster/interrupt.h" #include "postmaster/interrupt.h"
#include "replication/walreceiver.h" #include "replication/walreceiver.h"
#include "replication/walsender.h" #include "replication/walsender.h"
@ -179,7 +180,7 @@ ProcessWalRcvInterrupts(void)
/* Main entry point for walreceiver process */ /* Main entry point for walreceiver process */
void void
WalReceiverMain(void) WalReceiverMain(char *startup_data, size_t startup_data_len)
{ {
char conninfo[MAXCONNINFO]; char conninfo[MAXCONNINFO];
char *tmp_conninfo; char *tmp_conninfo;
@ -195,6 +196,11 @@ WalReceiverMain(void)
char *sender_host = NULL; char *sender_host = NULL;
int sender_port = 0; int sender_port = 0;
Assert(startup_data_len == 0);
MyBackendType = B_WAL_RECEIVER;
AuxiliaryProcessMainCommon();
/* /*
* WalRcv should be set up already (if we are a backend, we inherit this * WalRcv should be set up already (if we are a backend, we inherit this
* by fork() or EXEC_BACKEND mechanism from the postmaster). * by fork() or EXEC_BACKEND mechanism from the postmaster).

View File

@ -45,6 +45,7 @@ volatile uint32 CritSectionCount = 0;
int MyProcPid; int MyProcPid;
pg_time_t MyStartTime; pg_time_t MyStartTime;
TimestampTz MyStartTimestamp; TimestampTz MyStartTimestamp;
struct ClientSocket *MyClientSocket;
struct Port *MyProcPort; struct Port *MyProcPort;
int32 MyCancelKey; int32 MyCancelKey;
int MyPMChildSlot; int MyPMChildSlot;

View File

@ -50,18 +50,14 @@ extern PGDLLIMPORT int Log_autovacuum_min_duration;
/* Status inquiry functions */ /* Status inquiry functions */
extern bool AutoVacuumingActive(void); extern bool AutoVacuumingActive(void);
/* Functions to start autovacuum process, called from postmaster */ /* called from postmaster at server startup */
extern void autovac_init(void); extern void autovac_init(void);
extern int StartAutoVacLauncher(void);
extern int StartAutoVacWorker(void);
/* called from postmaster when a worker could not be forked */ /* called from postmaster when a worker could not be forked */
extern void AutoVacWorkerFailed(void); extern void AutoVacWorkerFailed(void);
#ifdef EXEC_BACKEND extern void AutoVacLauncherMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
extern void AutoVacLauncherMain(int argc, char *argv[]) pg_attribute_noreturn(); extern void AutoVacWorkerMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
extern void AutoVacWorkerMain(int argc, char *argv[]) pg_attribute_noreturn();
#endif
extern bool AutoVacuumRequestWork(AutoVacuumWorkItemType type, extern bool AutoVacuumRequestWork(AutoVacuumWorkItemType type,
Oid relationId, BlockNumber blkno); Oid relationId, BlockNumber blkno);

View File

@ -13,8 +13,6 @@
#ifndef AUXPROCESS_H #ifndef AUXPROCESS_H
#define AUXPROCESS_H #define AUXPROCESS_H
#include "miscadmin.h" extern void AuxiliaryProcessMainCommon(void);
extern void AuxiliaryProcessMain(BackendType auxtype) pg_attribute_noreturn();
#endif /* AUXPROCESS_H */ #endif /* AUXPROCESS_H */

View File

@ -55,6 +55,6 @@ extern void ForgetUnstartedBackgroundWorkers(void);
extern void ResetBackgroundWorkerCrashTimes(void); extern void ResetBackgroundWorkerCrashTimes(void);
/* Entry point for background worker processes */ /* 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 */ #endif /* BGWORKER_INTERNALS_H */

View File

@ -27,8 +27,8 @@ extern PGDLLIMPORT int CheckPointTimeout;
extern PGDLLIMPORT int CheckPointWarning; extern PGDLLIMPORT int CheckPointWarning;
extern PGDLLIMPORT double CheckPointCompletionTarget; extern PGDLLIMPORT double CheckPointCompletionTarget;
extern void BackgroundWriterMain(void) pg_attribute_noreturn(); extern void BackgroundWriterMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
extern void CheckpointerMain(void) pg_attribute_noreturn(); extern void CheckpointerMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
extern void RequestCheckpoint(int flags); extern void RequestCheckpoint(int flags);
extern void CheckpointWriteDelay(int flags, double progress); extern void CheckpointWriteDelay(int flags, double progress);

View File

@ -29,7 +29,7 @@
extern Size PgArchShmemSize(void); extern Size PgArchShmemSize(void);
extern void PgArchShmemInit(void); extern void PgArchShmemInit(void);
extern bool PgArchCanRestart(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 PgArchWakeup(void);
extern void PgArchForceDirScan(void); extern void PgArchForceDirScan(void);

View File

@ -13,6 +13,8 @@
#ifndef _POSTMASTER_H #ifndef _POSTMASTER_H
#define _POSTMASTER_H #define _POSTMASTER_H
#include "miscadmin.h"
/* GUC options */ /* GUC options */
extern PGDLLIMPORT bool EnableSSL; extern PGDLLIMPORT bool EnableSSL;
extern PGDLLIMPORT int SuperuserReservedConnections; extern PGDLLIMPORT int SuperuserReservedConnections;
@ -58,11 +60,9 @@ extern int MaxLivePostmasterChildren(void);
extern bool PostmasterMarkPIDForWorkerNotify(int); extern bool PostmasterMarkPIDForWorkerNotify(int);
extern void BackendMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
extern pid_t postmaster_forkexec(int argc, char *argv[]);
extern void SubPostmasterMain(int argc, char *argv[]) pg_attribute_noreturn();
extern Size ShmemBackendArraySize(void); extern Size ShmemBackendArraySize(void);
extern void ShmemBackendArrayAllocation(void); extern void ShmemBackendArrayAllocation(void);
@ -71,6 +71,16 @@ extern void pgwin32_register_deadchild_callback(HANDLE procHandle, DWORD procId)
#endif #endif
#endif #endif
/* defined in globals.c */
extern struct ClientSocket *MyClientSocket;
/* prototypes for functions in launch_backend.c */
extern pid_t postmaster_child_launch(BackendType child_type, char *startup_data, size_t startup_data_len, struct ClientSocket *sock);
const char *PostmasterChildName(BackendType child_type);
#ifdef EXEC_BACKEND
extern void SubPostmasterMain(int argc, char *argv[]) pg_attribute_noreturn();
#endif
/* /*
* Note: MAX_BACKENDS is limited to 2^18-1 because that's the width reserved * 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 * for buffer references in buf_internals.h. This limitation could be lifted

View File

@ -26,7 +26,7 @@
extern PGDLLIMPORT int log_startup_progress_interval; extern PGDLLIMPORT int log_startup_progress_interval;
extern void HandleStartupProcInterrupts(void); 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 PreRestoreCommand(void);
extern void PostRestoreCommand(void); extern void PostRestoreCommand(void);
extern bool IsPromoteSignaled(void); extern bool IsPromoteSignaled(void);

View File

@ -86,9 +86,7 @@ extern int SysLogger_Start(void);
extern void write_syslogger_file(const char *buffer, int count, int destination); extern void write_syslogger_file(const char *buffer, int count, int destination);
#ifdef EXEC_BACKEND extern void SysLoggerMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
extern void SysLoggerMain(int argc, char *argv[]) pg_attribute_noreturn();
#endif
extern bool CheckLogrotateSignal(void); extern bool CheckLogrotateSignal(void);
extern void RemoveLogrotateSignalFiles(void); extern void RemoveLogrotateSignalFiles(void);

View File

@ -21,7 +21,7 @@ extern PGDLLIMPORT int wal_summary_keep_time;
extern Size WalSummarizerShmemSize(void); extern Size WalSummarizerShmemSize(void);
extern void WalSummarizerShmemInit(void); extern void WalSummarizerShmemInit(void);
extern void WalSummarizerMain(void) pg_attribute_noreturn(); extern void WalSummarizerMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
extern void GetWalSummarizerState(TimeLineID *summarized_tli, extern void GetWalSummarizerState(TimeLineID *summarized_tli,
XLogRecPtr *summarized_lsn, XLogRecPtr *summarized_lsn,

View File

@ -18,6 +18,6 @@
extern PGDLLIMPORT int WalWriterDelay; extern PGDLLIMPORT int WalWriterDelay;
extern PGDLLIMPORT int WalWriterFlushAfter; 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 */ #endif /* _WALWRITER_H */

View File

@ -26,9 +26,7 @@ extern PGDLLIMPORT char *PrimarySlotName;
extern char *CheckAndGetDbnameFromConninfo(void); extern char *CheckAndGetDbnameFromConninfo(void);
extern bool ValidateSlotSyncParams(int elevel); extern bool ValidateSlotSyncParams(int elevel);
#ifdef EXEC_BACKEND extern void ReplSlotSyncWorkerMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
extern void ReplSlotSyncWorkerMain(int argc, char *argv[]) pg_attribute_noreturn();
#endif
extern int StartSlotSyncWorker(void); extern int StartSlotSyncWorker(void);
extern void ShutDownSlotSync(void); extern void ShutDownSlotSync(void);

View File

@ -483,7 +483,7 @@ walrcv_clear_result(WalRcvExecResult *walres)
} }
/* prototypes for functions in walreceiver.c */ /* 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 ProcessWalRcvInterrupts(void);
extern void WalRcvForceReply(void); extern void WalRcvForceReply(void);

View File

@ -231,6 +231,7 @@ BY_HANDLE_FILE_INFORMATION
Backend Backend
BackendId BackendId
BackendParameters BackendParameters
BackendStartupData
BackendState BackendState
BackendType BackendType
BackgroundWorker BackgroundWorker
@ -2136,6 +2137,7 @@ PortalStrategy
PostParseColumnRefHook PostParseColumnRefHook
PostgresPollingStatusType PostgresPollingStatusType
PostingItem PostingItem
PostmasterChildType
PreParseColumnRefHook PreParseColumnRefHook
PredClass PredClass
PredIterInfo PredIterInfo
@ -3259,6 +3261,7 @@ check_network_data
check_object_relabel_type check_object_relabel_type
check_password_hook_type check_password_hook_type
check_ungrouped_columns_context check_ungrouped_columns_context
child_process_kind
chr chr
cmpEntriesArg cmpEntriesArg
codes_t codes_t
@ -4042,6 +4045,7 @@ BlockRefTableReader
BlockRefTableSerializedEntry BlockRefTableSerializedEntry
BlockRefTableWriter BlockRefTableWriter
SummarizerReadLocalXLogPrivate SummarizerReadLocalXLogPrivate
SysloggerStartupData
WalSummarizerData WalSummarizerData
WalSummaryFile WalSummaryFile
WalSummaryIO WalSummaryIO