diff --git a/src/backend/postmaster/Makefile b/src/backend/postmaster/Makefile index 367a46c617..db08543d19 100644 --- a/src/backend/postmaster/Makefile +++ b/src/backend/postmaster/Makefile @@ -20,6 +20,7 @@ OBJS = \ checkpointer.o \ fork_process.o \ interrupt.o \ + launch_backend.o \ pgarch.o \ postmaster.o \ startup.o \ diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c new file mode 100644 index 0000000000..80c96e708d --- /dev/null +++ b/src/backend/postmaster/launch_backend.c @@ -0,0 +1,811 @@ +/*------------------------------------------------------------------------- + * + * 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, reload the config file etc. to get the process to the + * same state as after fork() on a Unix system. + * + * + * 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 "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/fork_process.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/timestamp.h" + +#ifdef EXEC_BACKEND +#include "nodes/queryjumble.h" +#include "storage/pg_shmem.h" +#include "storage/spin.h" +#endif + + +#ifdef EXEC_BACKEND + +/* 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 +{ + bool has_client_sock; + ClientSocket client_sock; + InheritableSocket inh_sock; + + bool has_bgworker; + BackgroundWorker bgworker; + + char DataDir[MAXPGPATH]; + int32 MyCancelKey; + int MyPMChildSlot; +#ifndef WIN32 + unsigned long UsedShmemSegID; +#else + void *ShmemProtectiveRegion; + HANDLE UsedShmemSegID; +#endif + void *UsedShmemSegAddr; + slock_t *ShmemLock; + 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]; +} BackendParameters; + +#define SizeOfBackendParameters(startup_data_len) (offsetof(BackendParameters, startup_data) + startup_data_len) + +void read_backend_variables(char *id, ClientSocket **client_sock, BackgroundWorker **worker); +static void restore_backend_variables(BackendParameters *param, ClientSocket **client_sock, BackgroundWorker **worker); + +#ifndef WIN32 +static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock, BackgroundWorker *worker); +#else +static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock, BackgroundWorker *worker, + HANDLE childProcess, pid_t childPid); +#endif + +pid_t internal_forkexec(int argc, char *argv[], ClientSocket *client_sock, BackgroundWorker *worker); + +#ifndef WIN32 + +/* + * internal_forkexec non-win32 implementation + * + * - writes out backend variables to the parameter file + * - fork():s, and then exec():s the child process + */ +pid_t +internal_forkexec(int argc, char *argv[], ClientSocket *client_sock, BackgroundWorker *worker) +{ + static unsigned long tmpBackendFileNum = 0; + pid_t pid; + char tmpfilename[MAXPGPATH]; + BackendParameters param; + FILE *fp; + + /* + * Make sure padding bytes are initialized, to prevent Valgrind from + * complaining about writing uninitialized bytes to the file. This isn't + * performance critical, and the win32 implementation initializes the + * padding bytes to zeros, so do it even when not using Valgrind. + */ + memset(¶m, 0, sizeof(BackendParameters)); + + if (!save_backend_variables(¶m, client_sock, worker)) + return -1; /* log made by save_backend_variables */ + + /* Calculate name for temp file */ + snprintf(tmpfilename, MAXPGPATH, "%s/%s.backend_var.%d.%lu", + PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX, + MyProcPid, ++tmpBackendFileNum); + + /* Open file */ + fp = AllocateFile(tmpfilename, PG_BINARY_W); + if (!fp) + { + /* + * As in OpenTemporaryFileInTablespace, try to make the temp-file + * directory, ignoring errors. + */ + (void) MakePGDirectory(PG_TEMP_FILES_DIR); + + fp = AllocateFile(tmpfilename, PG_BINARY_W); + if (!fp) + { + ereport(LOG, + (errcode_for_file_access(), + errmsg("could not create file \"%s\": %m", + tmpfilename))); + return -1; + } + } + + if (fwrite(¶m, sizeof(param), 1, fp) != 1) + { + ereport(LOG, + (errcode_for_file_access(), + errmsg("could not write to file \"%s\": %m", tmpfilename))); + FreeFile(fp); + return -1; + } + + /* Release file */ + if (FreeFile(fp)) + { + ereport(LOG, + (errcode_for_file_access(), + errmsg("could not write to file \"%s\": %m", tmpfilename))); + return -1; + } + + /* 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; + + /* 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. + */ +pid_t +internal_forkexec(int argc, char *argv[], ClientSocket *client_sock, BackgroundWorker *worker) +{ + 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]; + + /* 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) + { + ereport(LOG, + (errmsg("could not create backend parameter file mapping: error code %lu", + GetLastError()))); + return -1; + } + + param = MapViewOfFile(paramHandle, FILE_MAP_WRITE, 0, 0, sizeof(BackendParameters)); + if (!param) + { + ereport(LOG, + (errmsg("could not map backend parameter memory: error code %lu", + GetLastError()))); + CloseHandle(paramHandle); + return -1; + } + + /* Insert temp file name after --fork argument */ +#ifdef _WIN64 + sprintf(paramHandleStr, "%llu", (LONG_PTR) paramHandle); +#else + sprintf(paramHandleStr, "%lu", (DWORD) paramHandle); +#endif + 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') + { + 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, worker, 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; + } + + /* Set up notification when the child process dies */ + pgwin32_register_deadchild_callback(pi.hProcess, pi.dwProcessId); + + /* Don't close pi.hProcess, it's owned by the deadchild callback now */ + + CloseHandle(pi.hThread); + + return pi.dwProcessId; +} +#endif /* WIN32 */ + +/* + * The following need to be available to the save/restore_backend_variables + * functions. They are marked NON_EXEC_STATIC in their home modules. + */ +extern slock_t *ShmemLock; +extern slock_t *ProcStructLock; +extern PGPROC *AuxiliaryProcs; +extern PMSignalData *PMSignalState; +extern pg_time_t first_syslogger_file_time; +extern struct bkend *ShmemBackendArray; +extern bool redirection_done; + +#ifndef WIN32 +#define write_inheritable_socket(dest, src, childpid) ((*(dest) = (src)), true) +#define read_inheritable_socket(dest, src) (*(dest) = *(src)) +#else +static bool write_duplicated_handle(HANDLE *dest, HANDLE src, HANDLE child); +static bool write_inheritable_socket(InheritableSocket *dest, SOCKET src, + pid_t childPid); +static void read_inheritable_socket(SOCKET *dest, InheritableSocket *src); +#endif + + +/* Save critical backend variables into the BackendParameters struct */ +#ifndef WIN32 +static bool +save_backend_variables(BackendParameters *param, ClientSocket *client_sock, BackgroundWorker *worker) +#else +static bool +save_backend_variables(BackendParameters *param, ClientSocket *client_sock, BackgroundWorker *worker, + HANDLE childProcess, pid_t childPid) +#endif +{ + if (client_sock) + { + memcpy(¶m->client_sock, client_sock, sizeof(ClientSocket)); + if (!write_inheritable_socket(¶m->inh_sock, client_sock->sock, childPid)) + return false; + param->has_client_sock = true; + } + else + { + memset(¶m->client_sock, 0, sizeof(ClientSocket)); + param->has_client_sock = false; + } + + if (worker) + { + memcpy(¶m->bgworker, worker, sizeof(BackgroundWorker)); + param->has_bgworker = true; + } + else + { + memset(¶m->bgworker, 0, sizeof(BackgroundWorker)); + param->has_bgworker = 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->ShmemBackendArray = ShmemBackendArray; + +#ifndef HAVE_SPINLOCKS + param->SpinlockSemaArray = SpinlockSemaArray; +#endif + param->NamedLWLockTrancheRequests = NamedLWLockTrancheRequests; + param->NamedLWLockTrancheArray = NamedLWLockTrancheArray; + param->MainLWLockArray = MainLWLockArray; + param->ProcStructLock = ProcStructLock; + param->ProcGlobal = ProcGlobal; + param->AuxiliaryProcs = AuxiliaryProcs; + param->PreparedXactProcs = PreparedXactProcs; + param->PMSignalState = PMSignalState; + + param->PostmasterPid = PostmasterPid; + param->PgStartTime = PgStartTime; + param->PgReloadTime = PgReloadTime; + param->first_syslogger_file_time = first_syslogger_file_time; + + param->redirection_done = redirection_done; + param->IsBinaryUpgrade = IsBinaryUpgrade; + param->query_id_enabled = query_id_enabled; + param->max_safe_fds = max_safe_fds; + + param->MaxBackends = MaxBackends; + +#ifdef WIN32 + param->PostmasterHandle = PostmasterHandle; + if (!write_duplicated_handle(¶m->initial_signal_pipe, + pgwin32_create_signal_listener(childPid), + childProcess)) + return false; +#else + memcpy(¶m->postmaster_alive_fds, &postmaster_alive_fds, + sizeof(postmaster_alive_fds)); +#endif + + memcpy(¶m->syslogPipe, &syslogPipe, sizeof(syslogPipe)); + + strlcpy(param->my_exec_path, my_exec_path, MAXPGPATH); + + strlcpy(param->pkglib_path, pkglib_path, MAXPGPATH); + + return true; +} + +#ifdef WIN32 +/* + * Duplicate a handle for usage in a child process, and write the child + * process instance of the handle to the parameter file. + */ +static bool +write_duplicated_handle(HANDLE *dest, HANDLE src, HANDLE childProcess) +{ + HANDLE hChild = INVALID_HANDLE_VALUE; + + if (!DuplicateHandle(GetCurrentProcess(), + src, + childProcess, + &hChild, + 0, + TRUE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) + { + ereport(LOG, + (errmsg_internal("could not duplicate handle to be written to backend parameter file: error code %lu", + GetLastError()))); + return false; + } + + *dest = hChild; + return true; +} + +/* + * Duplicate a socket for usage in a child process, and write the resulting + * structure to the parameter file. + * This is required because a number of LSPs (Layered Service Providers) very + * common on Windows (antivirus, firewalls, download managers etc) break + * straight socket inheritance. + */ +static bool +write_inheritable_socket(InheritableSocket *dest, SOCKET src, pid_t childpid) +{ + dest->origsocket = src; + if (src != 0 && src != PGINVALID_SOCKET) + { + /* Actual socket */ + if (WSADuplicateSocket(src, childpid, &dest->wsainfo) != 0) + { + ereport(LOG, + (errmsg("could not duplicate socket %d for use in backend: error code %d", + (int) src, WSAGetLastError()))); + return false; + } + } + return true; +} + +/* + * Read a duplicate socket structure back, and get the socket descriptor. + */ +static void +read_inheritable_socket(SOCKET *dest, InheritableSocket *src) +{ + SOCKET s; + + if (src->origsocket == PGINVALID_SOCKET || src->origsocket == 0) + { + /* Not a real socket! */ + *dest = src->origsocket; + } + else + { + /* Actual socket, so create from structure */ + s = WSASocket(FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + &src->wsainfo, + 0, + 0); + if (s == INVALID_SOCKET) + { + write_stderr("could not create inherited socket: error code %d\n", + WSAGetLastError()); + exit(1); + } + *dest = s; + + /* + * To make sure we don't get two references to the same socket, close + * the original one. (This would happen when inheritance actually + * works.. + */ + closesocket(src->origsocket); + } +} +#endif + +void +read_backend_variables(char *id, ClientSocket **client_sock, BackgroundWorker **worker) +{ + 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\": %m\n", id); + exit(1); + } + + if (fread(¶m, sizeof(param), 1, fp) != 1) + { + write_stderr("could not read from backend variables file \"%s\": %m\n", id); + exit(1); + } + + /* Release file */ + FreeFile(fp); + if (unlink(id) != 0) + { + write_stderr("could not remove file \"%s\": %m\n", id); + 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, worker); +} + +/* Restore critical backend variables from the BackendParameters struct */ +static void +restore_backend_variables(BackendParameters *param, ClientSocket **client_sock, BackgroundWorker **worker) +{ + if (param->has_client_sock) + { + *client_sock = (ClientSocket *) MemoryContextAlloc(TopMemoryContext, sizeof(ClientSocket)); + memcpy(*client_sock, ¶m->client_sock, sizeof(ClientSocket)); + read_inheritable_socket(&(*client_sock)->sock, ¶m->inh_sock); + } + else + *client_sock = NULL; + + if (param->has_bgworker) + { + *worker = (BackgroundWorker *) + MemoryContextAlloc(TopMemoryContext, sizeof(BackgroundWorker)); + memcpy(*worker, ¶m->bgworker, sizeof(BackgroundWorker)); + } + else + *worker = NULL; + + SetDataDir(param->DataDir); + + MyCancelKey = param->MyCancelKey; + MyPMChildSlot = param->MyPMChildSlot; + +#ifdef WIN32 + ShmemProtectiveRegion = param->ShmemProtectiveRegion; +#endif + UsedShmemSegID = param->UsedShmemSegID; + UsedShmemSegAddr = param->UsedShmemSegAddr; + + ShmemLock = param->ShmemLock; + 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 63a611b68b..0ea4bbe084 100644 --- a/src/backend/postmaster/meson.build +++ b/src/backend/postmaster/meson.build @@ -8,6 +8,7 @@ backend_sources += files( 'checkpointer.c', 'fork_process.c', 'interrupt.c', + 'launch_backend.c', 'pgarch.c', 'postmaster.c', 'startup.c', diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index a373e82dd6..6610bd5f86 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -129,9 +129,7 @@ #include "utils/varlena.h" #ifdef EXEC_BACKEND -#include "nodes/queryjumble.h" #include "storage/pg_shmem.h" -#include "storage/spin.h" #endif @@ -186,7 +184,7 @@ typedef struct bkend static dlist_head BackendList = DLIST_STATIC_INIT(BackendList); #ifdef EXEC_BACKEND -static Backend *ShmemBackendArray; +Backend *ShmemBackendArray; #endif BackgroundWorker *MyBgworkerEntry = NULL; @@ -476,7 +474,6 @@ static void MaybeStartSlotSyncWorker(void); static pid_t waitpid(pid_t pid, int *exitstatus, int options); static void WINAPI pgwin32_deadchild_callback(PVOID lpParameter, BOOLEAN TimerOrWaitFired); -static void pgwin32_register_deadchild_callback(HANDLE procHandle, DWORD procId); static HANDLE win32ChildQueue; @@ -489,85 +486,11 @@ typedef struct #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, BackgroundWorker *worker); -/* 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 -{ - bool has_client_sock; - ClientSocket client_sock; - InheritableSocket inh_sock; - - bool has_bgworker; - BackgroundWorker bgworker; - - char DataDir[MAXPGPATH]; - int32 MyCancelKey; - int MyPMChildSlot; -#ifndef WIN32 - unsigned long UsedShmemSegID; -#else - void *ShmemProtectiveRegion; - HANDLE UsedShmemSegID; -#endif - void *UsedShmemSegAddr; - slock_t *ShmemLock; - 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]; -} BackendParameters; - -static void read_backend_variables(char *id, ClientSocket **client_sock, BackgroundWorker **worker); -static void restore_backend_variables(BackendParameters *param, ClientSocket **client_sock, BackgroundWorker **worker); - -#ifndef WIN32 -static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock, BackgroundWorker *worker); -#else -static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock, BackgroundWorker *worker, - HANDLE childProcess, pid_t childPid); -#endif +/* in launch_backend.c */ +extern pid_t internal_forkexec(int argc, char *argv[], ClientSocket *client_sock, BackgroundWorker *worker); +extern void read_backend_variables(char *id, ClientSocket **client_sock, BackgroundWorker **worker); static void ShmemBackendArrayAdd(Backend *bn); static void ShmemBackendArrayRemove(Backend *bn); @@ -1117,11 +1040,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 @@ -4503,298 +4426,6 @@ backend_forkexec(ClientSocket *client_sock, CAC_state cac) return internal_forkexec(ac, av, client_sock, NULL); } -#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(int argc, char *argv[], ClientSocket *client_sock, BackgroundWorker *worker) -{ - static unsigned long tmpBackendFileNum = 0; - pid_t pid; - char tmpfilename[MAXPGPATH]; - BackendParameters param; - FILE *fp; - - /* - * Make sure padding bytes are initialized, to prevent Valgrind from - * complaining about writing uninitialized bytes to the file. This isn't - * performance critical, and the win32 implementation initializes the - * padding bytes to zeros, so do it even when not using Valgrind. - */ - memset(¶m, 0, sizeof(BackendParameters)); - - if (!save_backend_variables(¶m, client_sock, worker)) - return -1; /* log made by save_backend_variables */ - - /* Calculate name for temp file */ - snprintf(tmpfilename, MAXPGPATH, "%s/%s.backend_var.%d.%lu", - PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX, - MyProcPid, ++tmpBackendFileNum); - - /* Open file */ - fp = AllocateFile(tmpfilename, PG_BINARY_W); - if (!fp) - { - /* - * As in OpenTemporaryFileInTablespace, try to make the temp-file - * directory, ignoring errors. - */ - (void) MakePGDirectory(PG_TEMP_FILES_DIR); - - fp = AllocateFile(tmpfilename, PG_BINARY_W); - if (!fp) - { - ereport(LOG, - (errcode_for_file_access(), - errmsg("could not create file \"%s\": %m", - tmpfilename))); - return -1; - } - } - - if (fwrite(¶m, sizeof(param), 1, fp) != 1) - { - ereport(LOG, - (errcode_for_file_access(), - errmsg("could not write to file \"%s\": %m", tmpfilename))); - FreeFile(fp); - return -1; - } - - /* Release file */ - if (FreeFile(fp)) - { - ereport(LOG, - (errcode_for_file_access(), - errmsg("could not write to file \"%s\": %m", tmpfilename))); - return -1; - } - - /* 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; - - /* 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(int argc, char *argv[], ClientSocket *client_sock, BackgroundWorker *worker) -{ - 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]; - - /* 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) - { - ereport(LOG, - (errmsg("could not create backend parameter file mapping: error code %lu", - GetLastError()))); - return -1; - } - - param = MapViewOfFile(paramHandle, FILE_MAP_WRITE, 0, 0, sizeof(BackendParameters)); - if (!param) - { - ereport(LOG, - (errmsg("could not map backend parameter memory: error code %lu", - GetLastError()))); - CloseHandle(paramHandle); - return -1; - } - - /* Insert temp file name after --fork argument */ -#ifdef _WIN64 - sprintf(paramHandleStr, "%llu", (LONG_PTR) paramHandle); -#else - sprintf(paramHandleStr, "%lu", (DWORD) paramHandle); -#endif - 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') - { - 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, worker, 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; - } - - /* Set up notification when the child process dies */ - pgwin32_register_deadchild_callback(pi.hProcess, pi.dwProcessId); - - /* Don't close pi.hProcess, it's owned by the deadchild callback now */ - - 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 @@ -6026,369 +5657,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, BackgroundWorker *worker) -#else -static bool -save_backend_variables(BackendParameters *param, ClientSocket *client_sock, BackgroundWorker *worker, - HANDLE childProcess, pid_t childPid) -#endif -{ - if (client_sock) - { - memcpy(¶m->client_sock, client_sock, sizeof(ClientSocket)); - if (!write_inheritable_socket(¶m->inh_sock, client_sock->sock, childPid)) - return false; - param->has_client_sock = true; - } - else - { - memset(¶m->client_sock, 0, sizeof(ClientSocket)); - param->has_client_sock = false; - } - - if (worker) - { - memcpy(¶m->bgworker, worker, sizeof(BackgroundWorker)); - param->has_bgworker = true; - } - else - { - memset(¶m->bgworker, 0, sizeof(BackgroundWorker)); - param->has_bgworker = 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->ShmemBackendArray = ShmemBackendArray; - -#ifndef HAVE_SPINLOCKS - param->SpinlockSemaArray = SpinlockSemaArray; -#endif - param->NamedLWLockTrancheRequests = NamedLWLockTrancheRequests; - param->NamedLWLockTrancheArray = NamedLWLockTrancheArray; - param->MainLWLockArray = MainLWLockArray; - param->ProcStructLock = ProcStructLock; - param->ProcGlobal = ProcGlobal; - param->AuxiliaryProcs = AuxiliaryProcs; - param->PreparedXactProcs = PreparedXactProcs; - param->PMSignalState = PMSignalState; - - param->PostmasterPid = PostmasterPid; - param->PgStartTime = PgStartTime; - param->PgReloadTime = PgReloadTime; - param->first_syslogger_file_time = first_syslogger_file_time; - - param->redirection_done = redirection_done; - param->IsBinaryUpgrade = IsBinaryUpgrade; - param->query_id_enabled = query_id_enabled; - param->max_safe_fds = max_safe_fds; - - param->MaxBackends = MaxBackends; - -#ifdef WIN32 - param->PostmasterHandle = PostmasterHandle; - if (!write_duplicated_handle(¶m->initial_signal_pipe, - pgwin32_create_signal_listener(childPid), - childProcess)) - return false; -#else - memcpy(¶m->postmaster_alive_fds, &postmaster_alive_fds, - sizeof(postmaster_alive_fds)); -#endif - - memcpy(¶m->syslogPipe, &syslogPipe, sizeof(syslogPipe)); - - strlcpy(param->my_exec_path, my_exec_path, MAXPGPATH); - - strlcpy(param->pkglib_path, pkglib_path, MAXPGPATH); - - return true; -} - - -#ifdef WIN32 -/* - * Duplicate a handle for usage in a child process, and write the child - * process instance of the handle to the parameter file. - */ -static bool -write_duplicated_handle(HANDLE *dest, HANDLE src, HANDLE childProcess) -{ - HANDLE hChild = INVALID_HANDLE_VALUE; - - if (!DuplicateHandle(GetCurrentProcess(), - src, - childProcess, - &hChild, - 0, - TRUE, - DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) - { - ereport(LOG, - (errmsg_internal("could not duplicate handle to be written to backend parameter file: error code %lu", - GetLastError()))); - return false; - } - - *dest = hChild; - return true; -} - -/* - * Duplicate a socket for usage in a child process, and write the resulting - * structure to the parameter file. - * This is required because a number of LSPs (Layered Service Providers) very - * common on Windows (antivirus, firewalls, download managers etc) break - * straight socket inheritance. - */ -static bool -write_inheritable_socket(InheritableSocket *dest, SOCKET src, pid_t childpid) -{ - dest->origsocket = src; - if (src != 0 && src != PGINVALID_SOCKET) - { - /* Actual socket */ - if (WSADuplicateSocket(src, childpid, &dest->wsainfo) != 0) - { - ereport(LOG, - (errmsg("could not duplicate socket %d for use in backend: error code %d", - (int) src, WSAGetLastError()))); - return false; - } - } - return true; -} - -/* - * Read a duplicate socket structure back, and get the socket descriptor. - */ -static void -read_inheritable_socket(SOCKET *dest, InheritableSocket *src) -{ - SOCKET s; - - if (src->origsocket == PGINVALID_SOCKET || src->origsocket == 0) - { - /* Not a real socket! */ - *dest = src->origsocket; - } - else - { - /* Actual socket, so create from structure */ - s = WSASocket(FROM_PROTOCOL_INFO, - FROM_PROTOCOL_INFO, - FROM_PROTOCOL_INFO, - &src->wsainfo, - 0, - 0); - if (s == INVALID_SOCKET) - { - write_stderr("could not create inherited socket: error code %d\n", - WSAGetLastError()); - exit(1); - } - *dest = s; - - /* - * To make sure we don't get two references to the same socket, close - * the original one. (This would happen when inheritance actually - * works.. - */ - closesocket(src->origsocket); - } -} -#endif - -static void -read_backend_variables(char *id, ClientSocket **client_sock, BackgroundWorker **worker) -{ - 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\": %m\n", id); - exit(1); - } - - if (fread(¶m, sizeof(param), 1, fp) != 1) - { - write_stderr("could not read from backend variables file \"%s\": %m\n", id); - exit(1); - } - - /* Release file */ - FreeFile(fp); - if (unlink(id) != 0) - { - write_stderr("could not remove file \"%s\": %m\n", id); - 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, worker); -} - -/* Restore critical backend variables from the BackendParameters struct */ -static void -restore_backend_variables(BackendParameters *param, ClientSocket **client_sock, BackgroundWorker **worker) -{ - if (param->has_client_sock) - { - *client_sock = (ClientSocket *) MemoryContextAlloc(TopMemoryContext, sizeof(ClientSocket)); - memcpy(*client_sock, ¶m->client_sock, sizeof(ClientSocket)); - read_inheritable_socket(&(*client_sock)->sock, ¶m->inh_sock); - } - else - *client_sock = NULL; - - if (param->has_bgworker) - { - *worker = (BackgroundWorker *) - MemoryContextAlloc(TopMemoryContext, sizeof(BackgroundWorker)); - memcpy(*worker, ¶m->bgworker, sizeof(BackgroundWorker)); - } - else - *worker = NULL; - - SetDataDir(param->DataDir); - - MyCancelKey = param->MyCancelKey; - MyPMChildSlot = param->MyPMChildSlot; - -#ifdef WIN32 - ShmemProtectiveRegion = param->ShmemProtectiveRegion; -#endif - UsedShmemSegID = param->UsedShmemSegID; - UsedShmemSegAddr = param->UsedShmemSegAddr; - - ShmemLock = param->ShmemLock; - 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 -} - - Size ShmemBackendArraySize(void) { @@ -6516,7 +5784,7 @@ pgwin32_deadchild_callback(PVOID lpParameter, BOOLEAN TimerOrWaitFired) * automatically by an operating system thread pool. The memory and the * process handle will be freed by a later call to waitpid(). */ -static void +void pgwin32_register_deadchild_callback(HANDLE procHandle, DWORD procId) { win32_deadchild_waitinfo *childinfo; diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h index 46f95f7c8a..53f82bb5d4 100644 --- a/src/include/postmaster/postmaster.h +++ b/src/include/postmaster/postmaster.h @@ -59,11 +59,16 @@ 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 Size ShmemBackendArraySize(void); extern void ShmemBackendArrayAllocation(void); + +#ifdef WIN32 +extern void pgwin32_register_deadchild_callback(HANDLE procHandle, DWORD procId); +#endif #endif /*