#include "postgres.h"
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/wait.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <sys/param.h>
#include <netdb.h>
#include <limits.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef USE_BONJOUR
#include <dns_sd.h>
#endif
#ifdef USE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
#ifdef HAVE_PTHREAD_IS_THREADED_NP
#include <pthread.h>
#endif
#include "access/transam.h"
#include "access/xlog.h"
#include "bootstrap/bootstrap.h"
#include "catalog/pg_control.h"
#include "common/file_perm.h"
#include "common/ip.h"
#include "common/string.h"
#include "lib/ilist.h"
#include "libpq/auth.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "pg_getopt.h"
#include "pgstat.h"
#include "port/pg_bswap.h"
#include "postmaster/autovacuum.h"
#include "postmaster/bgworker_internals.h"
#include "postmaster/fork_process.h"
#include "postmaster/interrupt.h"
#include "postmaster/pgarch.h"
#include "postmaster/postmaster.h"
#include "postmaster/syslogger.h"
#include "replication/logicallauncher.h"
#include "replication/walsender.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/memutils.h"
#include "utils/pidfile.h"
#include "utils/ps_status.h"
#include "utils/timeout.h"
#include "utils/timestamp.h"
#include "utils/varlena.h"
#ifdef EXEC_BACKEND
#include "storage/spin.h"
#endif
#define BACKEND_TYPE_NORMAL 0x0001
#define BACKEND_TYPE_AUTOVAC 0x0002
#define BACKEND_TYPE_WALSND 0x0004
#define BACKEND_TYPE_BGWORKER 0x0008
#define BACKEND_TYPE_ALL 0x000F
typedef struct bkend
{
pid_t pid;
int32 cancel_key;
int child_slot;
int bkend_type;
bool dead_end;
bool bgworker_notify;
dlist_node elem;
} Backend;
#ifdef EXEC_BACKEND
static Backend *ShmemBackendArray;
#endif
#define MAXLISTEN 64
typedef enum
{
STARTUP_NOT_RUNNING,
STARTUP_RUNNING,
STARTUP_SIGNALED,
STARTUP_CRASHED
} StartupStatusEnum;
#define NoShutdown 0
#define SmartShutdown 1
#define FastShutdown 2
#define ImmediateShutdown 3
typedef enum
{
PM_INIT,
PM_STARTUP,
PM_RECOVERY,
PM_HOT_STANDBY,
PM_RUN,
PM_STOP_BACKENDS,
PM_WAIT_BACKENDS,
PM_SHUTDOWN,
PM_SHUTDOWN_2,
PM_WAIT_DEAD_END,
PM_NO_CHILDREN
} PMState;
typedef enum
{
ALLOW_ALL_CONNS,
ALLOW_SUPERUSER_CONNS,
ALLOW_NO_CONNS
} ConnsAllowedState;
#define SIGKILL_CHILDREN_AFTER_SECS 5
__thread bool ClientAuthInProgress = false;
#ifdef USE_SSL
static bool LoadedSSL = false;
#endif
#ifdef USE_BONJOUR
static DNSServiceRef bonjour_sdref = NULL;
#endif
static void CloseServerPorts(int status, Datum arg);
static void unlink_external_pid_file(int status, Datum arg);
static void getInstallationPaths(const char *argv0);
static void checkControlFile(void);
static Port *ConnCreate(int serverFd);
static void ConnFree(Port *port);
static void reset_shared(void);
static void SIGHUP_handler(SIGNAL_ARGS);
static void pmdie(SIGNAL_ARGS);
static void reaper(SIGNAL_ARGS);
static void sigusr1_handler(SIGNAL_ARGS);
static void process_startup_packet_die(SIGNAL_ARGS);
static void dummy_handler(SIGNAL_ARGS);
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,
int pid, int exitstatus);
static void PostmasterStateMachine(void);
static void BackendInitialize(Port *port);
static void BackendRun(Port *port) pg_attribute_noreturn();
static void ExitPostmaster(int status) pg_attribute_noreturn();
static int ServerLoop(void);
static int BackendStartup(Port *port);
static int ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done);
static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options);
static void processCancelRequest(Port *port, void *pkt);
static int initMasks(fd_set *rmask);
static void report_fork_failure_to_client(Port *port, int errnum);
static CAC_state canAcceptConnections(int backend_type);
static bool RandomCancelKey(int32 *cancel_key);
static void signal_child(pid_t pid, int signal);
static bool SignalSomeChildren(int signal, int targets);
static void TerminateChildren(int signal);
#define SignalChildren(sig) SignalSomeChildren(sig, BACKEND_TYPE_ALL)
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 void StartAutovacuumWorker(void);
static void MaybeStartWalReceiver(void);
static void InitPostmasterDeathWatchHandle(void);
#define PgArchStartupAllowed() \
((XLogArchivingActive() && pmState == PM_RUN) || \
(XLogArchivingAlways() && \
(pmState == PM_RECOVERY || pmState == PM_HOT_STANDBY)))
#ifdef EXEC_BACKEND
#ifdef WIN32
#define WNOHANG 0
static pid_t waitpid(pid_t pid, int *exitstatus, int options);
static void WINAPI pgwin32_deadchild_callback(PVOID lpParameter, BOOLEAN TimerOrWaitFired);
static HANDLE win32ChildQueue;
typedef struct
{
HANDLE waitHandle;
HANDLE procHandle;
DWORD procId;
} win32_deadchild_waitinfo;
#endif
static pid_t backend_forkexec(Port *port);
static pid_t internal_forkexec(int argc, char *argv[], Port *port);
#ifdef WIN32
typedef struct
{
SOCKET origsocket;
WSAPROTOCOL_INFO wsainfo;
} InheritableSocket;
#else
typedef int InheritableSocket;
#endif
typedef struct
{
Port port;
InheritableSocket portsocket;
char DataDir[MAXPGPATH];
pgsocket ListenSocket[MAXLISTEN];
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;
InheritableSocket pgStatSock;
pid_t PostmasterPid;
TimestampTz PgStartTime;
TimestampTz PgReloadTime;
pg_time_t first_syslogger_file_time;
bool redirection_done;
bool IsBinaryUpgrade;
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];
char ExtraOptions[MAXPGPATH];
} BackendParameters;
static void read_backend_variables(char *id, Port *port);
static void restore_backend_variables(BackendParameters *param, Port *port);
#ifndef WIN32
static bool save_backend_variables(BackendParameters *param, Port *port);
#else
static bool save_backend_variables(BackendParameters *param, Port *port,
HANDLE childProcess, pid_t childPid);
#endif
static void ShmemBackendArrayAdd(Backend *bn);
static void ShmemBackendArrayRemove(Backend *bn);
#endif
#define StartupDataBase() StartChildProcess(StartupProcess)
#define StartBackgroundWriter() StartChildProcess(BgWriterProcess)
#define StartCheckpointer() StartChildProcess(CheckpointerProcess)
#define StartWalWriter() StartChildProcess(WalWriterProcess)
#define StartWalReceiver() StartChildProcess(WalReceiverProcess)
#define EXIT_STATUS_0(st) ((st) == 0)
#define EXIT_STATUS_1(st) (WIFEXITED(st) && WEXITSTATUS(st) == 1)
#define EXIT_STATUS_3(st) (WIFEXITED(st) && WEXITSTATUS(st) == 3)
#ifndef WIN32
#else
HANDLE PostmasterHandle;
#endif
#ifdef SIGTTIN
#endif
#ifdef SIGTTOU
#endif
#ifdef SIGXFSZ
#endif
#ifdef HAVE_INT_OPTRESET
#endif
#ifdef USE_SSL
#endif
#ifdef WIN32
#endif
#ifdef EXEC_BACKEND
#endif
#ifdef USE_BONJOUR
#endif
#ifdef HAVE_UNIX_SOCKETS
#endif
#ifdef HAVE_PTHREAD_IS_THREADED_NP
#endif
#ifdef EXEC_BACKEND
#endif
#ifdef HAVE_PTHREAD_IS_THREADED_NP
#endif
#ifdef USE_SSL
#else
#endif
#ifdef USE_SSL
#endif
#ifdef ENABLE_GSS
#endif
#ifdef ENABLE_GSS
#endif
#ifndef EXEC_BACKEND
#else
#endif
#ifndef EXEC_BACKEND
#else
#endif
#ifndef EXEC_BACKEND
#else
#endif
#ifndef WIN32
#endif
#ifndef WIN32
#else
#endif
#ifdef USE_BONJOUR
#endif
#ifdef WIN32
#endif
#ifdef USE_SSL
#endif
#ifdef EXEC_BACKEND
#endif
#ifdef WIN32
#endif
#ifdef WIN32
#endif
#ifdef USE_SYSTEMD
#endif
#ifdef USE_SYSTEMD
#endif
#ifdef USE_SYSTEMD
#endif
#ifdef WIN32
#endif
#ifdef WIN32
#endif
#ifdef USE_SYSTEMD
#endif
#ifdef WIN32
#endif
#ifdef WIN32
#endif
#ifdef EXEC_BACKEND
#endif
#ifdef WIN32
#endif
#ifdef EXEC_BACKEND
#endif
#ifdef EXEC_BACKEND
#endif
#ifdef EXEC_BACKEND
#endif
#if defined(WIN32)
#else
#endif
#ifdef HAVE_SETSID
#endif
#ifdef EXEC_BACKEND
#else
#endif
#ifdef EXEC_BACKEND
#endif
#ifdef EXEC_BACKEND
pid_t
postmaster_forkexec(int argc, char *argv[])
{
Port port;
memset(&port, 0, sizeof(port));
return internal_forkexec(argc, argv, &port);
}
static pid_t
backend_forkexec(Port *port)
{
char *av[4];
int ac = 0;
av[ac++] = "postgres";
av[ac++] = "--forkbackend";
av[ac++] = NULL;
av[ac] = NULL;
Assert(ac < lengthof(av));
return internal_forkexec(ac, av, port);
}
#ifndef WIN32
static pid_t
internal_forkexec(int argc, char *argv[], Port *port)
{
static unsigned long tmpBackendFileNum = 0;
pid_t pid;
char tmpfilename[MAXPGPATH];
BackendParameters param;
FILE *fp;
if (!save_backend_variables(¶m, port))
return -1;
snprintf(tmpfilename, MAXPGPATH, "%s/%s.backend_var.%d.%lu",
PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX,
MyProcPid, ++tmpBackendFileNum);
fp = AllocateFile(tmpfilename, PG_BINARY_W);
if (!fp)
{
(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;
}
if (FreeFile(fp))
{
ereport(LOG,
(errcode_for_file_access(),
errmsg("could not write to file \"%s\": %m", tmpfilename)));
return -1;
}
Assert(argc >= 3);
Assert(argv[argc] == NULL);
Assert(strncmp(argv[1], "--fork", 6) == 0);
Assert(argv[2] == NULL);
argv[2] = tmpfilename;
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)));
exit(1);
}
}
return pid;
}
#else
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;
Assert(argc >= 3);
Assert(argv[argc] == NULL);
Assert(strncmp(argv[1], "--fork", 6) == 0);
Assert(argv[2] == NULL);
retry:
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)
{
elog(LOG, "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)
{
elog(LOG, "could not map backend parameter memory: error code %lu",
GetLastError());
CloseHandle(paramHandle);
return -1;
}
#ifdef _WIN64
sprintf(paramHandleStr, "%llu", (LONG_PTR) paramHandle);
#else
sprintf(paramHandleStr, "%lu", (DWORD) paramHandle);
#endif
argv[2] = paramHandleStr;
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')
{
elog(LOG, "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);
if (!CreateProcess(NULL, cmdLine, NULL, NULL, TRUE, CREATE_SUSPENDED,
NULL, NULL, &si, &pi))
{
elog(LOG, "CreateProcess call failed: %m (error code %lu)",
GetLastError());
UnmapViewOfFile(param);
CloseHandle(paramHandle);
return -1;
}
if (!save_backend_variables(param, port, pi.hProcess, pi.dwProcessId))
{
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;
}
if (!UnmapViewOfFile(param))
elog(LOG, "could not unmap view of backend parameter file: error code %lu",
GetLastError());
if (!CloseHandle(paramHandle))
elog(LOG, "could not close handle to backend parameter file: error code %lu",
GetLastError());
if (!pgwin32_ReserveSharedMemoryRegion(pi.hProcess))
{
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;
}
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;
}
childinfo = malloc(sizeof(win32_deadchild_waitinfo));
if (!childinfo)
ereport(FATAL,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
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())));
CloseHandle(pi.hThread);
return pi.dwProcessId;
}
#endif
void
SubPostmasterMain(int argc, char *argv[])
{
Port port;
IsPostmasterEnvironment = true;
whereToSendOutput = DestNone;
InitPostmasterChild();
InitializeGUCOptions();
if (argc < 3)
elog(FATAL, "invalid subpostmaster invocation");
memset(&port, 0, sizeof(Port));
read_backend_variables(argv[2], &port);
ClosePostmasterPorts(strcmp(argv[1], "--forklog") == 0);
if (strcmp(argv[1], "--forkbackend") == 0 ||
strcmp(argv[1], "--forkavlauncher") == 0 ||
strcmp(argv[1], "--forkavworker") == 0 ||
strcmp(argv[1], "--forkboot") == 0 ||
strncmp(argv[1], "--forkbgworker=", 15) == 0)
PGSharedMemoryReAttach();
else
PGSharedMemoryNoReAttach();
if (strcmp(argv[1], "--forkavlauncher") == 0)
AutovacuumLauncherIAm();
if (strcmp(argv[1], "--forkavworker") == 0)
AutovacuumWorkerIAm();
#ifdef WIN32
pgwin32_signal_initialize();
#endif
pqinitmask();
PG_SETMASK(&BlockSig);
read_nondefault_variables();
checkDataDir();
LocalProcessControlFile(false);
process_shared_preload_libraries();
if (strcmp(argv[1], "--forkbackend") == 0)
{
Assert(argc == 3);
#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
BackendInitialize(&port);
InitShmemAccess(UsedShmemSegAddr);
InitProcess();
CreateSharedMemoryAndSemaphores();
BackendRun(&port);
}
if (strcmp(argv[1], "--forkboot") == 0)
{
InitShmemAccess(UsedShmemSegAddr);
InitAuxiliaryProcess();
CreateSharedMemoryAndSemaphores();
AuxiliaryProcessMain(argc - 2, argv + 2);
}
if (strcmp(argv[1], "--forkavlauncher") == 0)
{
InitShmemAccess(UsedShmemSegAddr);
InitProcess();
CreateSharedMemoryAndSemaphores();
AutoVacLauncherMain(argc - 2, argv + 2);
}
if (strcmp(argv[1], "--forkavworker") == 0)
{
InitShmemAccess(UsedShmemSegAddr);
InitProcess();
CreateSharedMemoryAndSemaphores();
AutoVacWorkerMain(argc - 2, argv + 2);
}
if (strncmp(argv[1], "--forkbgworker=", 15) == 0)
{
int shmem_slot;
IsBackgroundWorker = true;
InitShmemAccess(UsedShmemSegAddr);
InitProcess();
CreateSharedMemoryAndSemaphores();
shmem_slot = atoi(argv[1] + 15);
MyBgworkerEntry = BackgroundWorkerEntry(shmem_slot);
StartBackgroundWorker();
}
if (strcmp(argv[1], "--forkarch") == 0)
{
PgArchiverMain(argc, argv);
}
if (strcmp(argv[1], "--forkcol") == 0)
{
PgstatCollectorMain(argc, argv);
}
if (strcmp(argv[1], "--forklog") == 0)
{
SysLoggerMain(argc, argv);
}
abort();
}
#endif
#ifdef HAVE_PTHREAD_IS_THREADED_NP
#endif
#ifdef WIN32
#endif
#ifdef USE_SYSTEMD
#endif
#ifdef USE_SYSTEMD
#endif
#ifdef WIN32
#endif
#ifdef EXEC_BACKEND
#endif
#ifdef EXEC_BACKEND
#else
#endif
#ifdef EXEC_BACKEND
#endif
#define OPTS_FILE "postmaster.opts"
#ifdef EXEC_BACKEND
static pid_t
bgworker_forkexec(int shmem_slot)
{
char *av[10];
int ac = 0;
char forkav[MAXPGPATH];
snprintf(forkav, MAXPGPATH, "--forkbgworker=%d", shmem_slot);
av[ac++] = "postgres";
av[ac++] = forkav;
av[ac++] = NULL;
av[ac] = NULL;
Assert(ac < lengthof(av));
return postmaster_forkexec(ac, av);
}
#endif
#ifdef EXEC_BACKEND
#else
#endif
#ifndef EXEC_BACKEND
#endif
#ifdef EXEC_BACKEND
#endif
#define MAX_BGWORKERS_TO_LAUNCH 100
#ifdef EXEC_BACKEND
extern slock_t *ShmemLock;
extern slock_t *ProcStructLock;
extern PGPROC *AuxiliaryProcs;
extern PMSignalData *PMSignalState;
extern pgsocket pgStatSock;
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
#ifndef WIN32
static bool
save_backend_variables(BackendParameters *param, Port *port)
#else
static bool
save_backend_variables(BackendParameters *param, Port *port,
HANDLE childProcess, pid_t childPid)
#endif
{
memcpy(¶m->port, port, sizeof(Port));
if (!write_inheritable_socket(¶m->portsocket, port->sock, childPid))
return false;
strlcpy(param->DataDir, DataDir, MAXPGPATH);
memcpy(¶m->ListenSocket, &ListenSocket, sizeof(ListenSocket));
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;
if (!write_inheritable_socket(¶m->pgStatSock, pgStatSock, childPid))
return false;
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->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);
strlcpy(param->ExtraOptions, ExtraOptions, MAXPGPATH);
return true;
}
#ifdef WIN32
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;
}
static bool
write_inheritable_socket(InheritableSocket *dest, SOCKET src, pid_t childpid)
{
dest->origsocket = src;
if (src != 0 && src != PGINVALID_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;
}
static void
read_inheritable_socket(SOCKET *dest, InheritableSocket *src)
{
SOCKET s;
if (src->origsocket == PGINVALID_SOCKET || src->origsocket == 0)
{
*dest = src->origsocket;
}
else
{
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;
closesocket(src->origsocket);
}
}
#endif
static void
read_backend_variables(char *id, Port *port)
{
BackendParameters param;
#ifndef WIN32
FILE *fp;
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);
}
FreeFile(fp);
if (unlink(id) != 0)
{
write_stderr("could not remove file \"%s\": %s\n",
id, strerror(errno));
exit(1);
}
#else
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, port);
}
static void
restore_backend_variables(BackendParameters *param, Port *port)
{
memcpy(port, ¶m->port, sizeof(Port));
read_inheritable_socket(&port->sock, ¶m->portsocket);
SetDataDir(param->DataDir);
memcpy(&ListenSocket, ¶m->ListenSocket, sizeof(ListenSocket));
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;
read_inheritable_socket(&pgStatSock, ¶m->pgStatSock);
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;
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);
strlcpy(ExtraOptions, param->ExtraOptions, MAXPGPATH);
#ifndef WIN32
if (postmaster_alive_fds[0] >= 0)
ReserveExternalFD();
if (postmaster_alive_fds[1] >= 0)
ReserveExternalFD();
#endif
if (pgStatSock != PGINVALID_SOCKET)
ReserveExternalFD();
}
Size
ShmemBackendArraySize(void)
{
return mul_size(MaxLivePostmasterChildren(), sizeof(Backend));
}
void
ShmemBackendArrayAllocation(void)
{
Size size = ShmemBackendArraySize();
ShmemBackendArray = (Backend *) ShmemAlloc(size);
memset(ShmemBackendArray, 0, size);
}
static void
ShmemBackendArrayAdd(Backend *bn)
{
int i = bn->child_slot - 1;
Assert(ShmemBackendArray[i].pid == 0);
ShmemBackendArray[i] = *bn;
}
static void
ShmemBackendArrayRemove(Backend *bn)
{
int i = bn->child_slot - 1;
Assert(ShmemBackendArray[i].pid == bn->pid);
ShmemBackendArray[i].pid = 0;
}
#endif
#ifdef WIN32
static pid_t
waitpid(pid_t pid, int *exitstatus, int options)
{
DWORD dwd;
ULONG_PTR key;
OVERLAPPED *ovl;
if (GetQueuedCompletionStatus(win32ChildQueue, &dwd, &key, &ovl, 0))
{
*exitstatus = (int) key;
return dwd;
}
return -1;
}
static void WINAPI
pgwin32_deadchild_callback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
{
win32_deadchild_waitinfo *childinfo = (win32_deadchild_waitinfo *) lpParameter;
DWORD exitcode;
if (TimerOrWaitFired)
return;
UnregisterWaitEx(childinfo->waitHandle, NULL);
if (!GetExitCodeProcess(childinfo->procHandle, &exitcode))
{
write_stderr("could not read exit code for process\n");
exitcode = 255;
}
if (!PostQueuedCompletionStatus(win32ChildQueue, childinfo->procId, (ULONG_PTR) exitcode, NULL))
write_stderr("could not post child completion status\n");
CloseHandle(childinfo->procHandle);
free(childinfo);
pg_queue_signal(SIGCHLD);
}
#endif
#ifndef WIN32
#else
#endif