From ce51876f87f1e4317e25baf64184749448fcd033 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Thu, 30 Nov 2023 00:07:34 +0200 Subject: [PATCH v3 7/7] 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 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 --- 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 | 1553 +++---------------- 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, 1787 insertions(+), 2002 deletions(-) delete mode 100644 src/backend/postmaster/fork_process.c create mode 100644 src/backend/postmaster/launch_backend.c delete mode 100644 src/include/postmaster/fork_process.h diff --git a/src/backend/postmaster/Makefile b/src/backend/postmaster/Makefile index 047448b34eb..fc88f5bae0b 100644 --- a/src/backend/postmaster/Makefile +++ b/src/backend/postmaster/Makefile @@ -18,8 +18,8 @@ OBJS = \ bgworker.o \ bgwriter.o \ checkpointer.o \ - fork_process.o \ interrupt.o \ + launch_backend.o \ pgarch.o \ postmaster.o \ startup.o \ diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 2f54485c217..0dda47ba300 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 or access any shared memory. @@ -1432,87 +1363,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; @@ -1543,6 +1409,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 or access any shared memory. diff --git a/src/backend/postmaster/auxprocess.c b/src/backend/postmaster/auxprocess.c index bae6f68c402..d4acd5146f4 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); @@ -122,7 +100,6 @@ AuxiliaryProcessMain(AuxProcType auxtype) */ CreateAuxProcessResourceOwner(); - /* Initialize backend status information */ pgstat_beinit(); pgstat_bestart(); @@ -131,37 +108,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 3fbc8f503cb..a27a658c025 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 d02dc17b9c1..027590f824b 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 42c807d3528..363c105cee6 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" @@ -169,11 +170,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 -#include -#include -#include -#include -#include - -#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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=", + * where 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 550136009a8..60ea739f7aa 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,58 @@ 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 or access any shared memory. + */ + InitProcess(); + + /* + * 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,729 +4333,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) { - /* - * Create a per-backend PGPROC struct in shared memory. We must do this - * before we can use LWLocks or access any shared memory. - */ - InitProcess(); +#ifdef HAVE_PTHREAD_IS_THREADED_NP /* - * Make sure we aren't in PostmasterContext anymore. (We can't delete it - * just yet, though, because InitPostgres will need the HBA data.) + * 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. */ - 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 */ + 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 - snprintf(cacbuf, sizeof(cacbuf), "%d", (int) cac); - av[ac++] = cacbuf; + /* should cleanup shared memory and kill all backends */ - av[ac] = NULL; - Assert(ac < lengthof(av)); + /* + * Not sure of the semantics here. When the Postmaster dies, should the + * backends all be killed? probably not. + * + * MUST -- vadim 05-10-1999 + */ - 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); - - 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 - * - * 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(); } /* @@ -5237,93 +4624,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; } @@ -5587,32 +4904,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. @@ -5649,65 +4940,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; } /* @@ -5959,351 +5217,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) { @@ -6425,6 +5338,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 0e7de26bc28..6b38acf922a 100644 --- a/src/backend/postmaster/startup.c +++ b/src/backend/postmaster/startup.c @@ -27,6 +27,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" @@ -242,8 +243,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 96dd03d9e06..c923843532f 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 48bc92205b5..89950350aee 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 26ded928a71..9b01132704b 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" @@ -183,7 +184,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; @@ -199,6 +200,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 60bc1217fb4..b6c3055027e 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 cfc5afaa6fd..0cec9e54861 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 f0cc651435c..a179cb39c40 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -319,7 +319,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 b553e858ad4..ee12e477f11 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 04b439dc503..4b76062255b 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 00649608e46..cfc3f259c95 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2121,6 +2121,7 @@ PortalStrategy PostParseColumnRefHook PostgresPollingStatusType PostingItem +PostmasterChildType PreParseColumnRefHook PredClass PredIterInfo @@ -3793,6 +3794,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