#include <time.h>
#include <sys/wait.h>
#include <ctype.h>
-#include <sys/select.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
static volatile bool StartWorkerNeeded = true;
static volatile bool HaveCrashedWorker = false;
+/* set when signals arrive */
+static volatile sig_atomic_t pending_pm_pmsignal;
+static volatile sig_atomic_t pending_pm_child_exit;
+static volatile sig_atomic_t pending_pm_reload_request;
+static volatile sig_atomic_t pending_pm_shutdown_request;
+static volatile sig_atomic_t pending_pm_fast_shutdown_request;
+static volatile sig_atomic_t pending_pm_immediate_shutdown_request;
+
+/* event multiplexing object */
+static WaitEventSet *pm_wait_set;
+
#ifdef USE_SSL
/* Set when and if SSL has been initialized properly */
static bool LoadedSSL = false;
static void checkControlFile(void);
static Port *ConnCreate(int serverFd);
static void ConnFree(Port *port);
-static void SIGHUP_handler(SIGNAL_ARGS);
-static void pmdie(SIGNAL_ARGS);
-static void reaper(SIGNAL_ARGS);
-static void sigusr1_handler(SIGNAL_ARGS);
+static void handle_pm_pmsignal_signal(SIGNAL_ARGS);
+static void handle_pm_child_exit_signal(SIGNAL_ARGS);
+static void handle_pm_reload_request_signal(SIGNAL_ARGS);
+static void handle_pm_shutdown_request_signal(SIGNAL_ARGS);
+static void process_pm_pmsignal(void);
+static void process_pm_child_exit(void);
+static void process_pm_reload_request(void);
+static void process_pm_shutdown_request(void);
static void process_startup_packet_die(SIGNAL_ARGS);
static void dummy_handler(SIGNAL_ARGS);
static void StartupPacketTimeoutHandler(void);
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);
/*
* Set up signal handlers for the postmaster process.
*
- * In the postmaster, we use pqsignal_pm() rather than pqsignal() (which
- * is used by all child processes and client processes). That has a
- * couple of special behaviors:
- *
- * 1. We tell sigaction() to block all signals for the duration of the
- * signal handler. This is faster than our old approach of
- * blocking/unblocking explicitly in the signal handler, and it should also
- * prevent excessive stack consumption if signals arrive quickly.
- *
- * 2. We do not set the SA_RESTART flag. This is because signals will be
- * blocked at all times except when ServerLoop is waiting for something to
- * happen, and during that window, we want signals to exit the select(2)
- * wait so that ServerLoop can respond if anything interesting happened.
- * On some platforms, signals marked SA_RESTART would not cause the
- * select() wait to end.
- *
- * Child processes will generally want SA_RESTART, so pqsignal() sets that
- * flag. We expect children to set up their own handlers before
- * unblocking signals.
- *
* CAUTION: when changing this list, check for side-effects on the signal
* handling setup of child processes. See tcop/postgres.c,
* bootstrap/bootstrap.c, postmaster/bgwriter.c, postmaster/walwriter.c,
pqinitmask();
PG_SETMASK(&BlockSig);
- pqsignal_pm(SIGHUP, SIGHUP_handler); /* reread config file and have
- * children do same */
- pqsignal_pm(SIGINT, pmdie); /* send SIGTERM and shut down */
- pqsignal_pm(SIGQUIT, pmdie); /* send SIGQUIT and die */
- pqsignal_pm(SIGTERM, pmdie); /* wait for children and shut down */
- pqsignal_pm(SIGALRM, SIG_IGN); /* ignored */
- pqsignal_pm(SIGPIPE, SIG_IGN); /* ignored */
- pqsignal_pm(SIGUSR1, sigusr1_handler); /* message from child process */
- pqsignal_pm(SIGUSR2, dummy_handler); /* unused, reserve for children */
- pqsignal_pm(SIGCHLD, reaper); /* handle child termination */
-
-#ifdef SIGURG
+ pqsignal(SIGHUP, handle_pm_reload_request_signal);
+ pqsignal(SIGINT, handle_pm_shutdown_request_signal);
+ pqsignal(SIGQUIT, handle_pm_shutdown_request_signal);
+ pqsignal(SIGTERM, handle_pm_shutdown_request_signal);
+ pqsignal(SIGALRM, SIG_IGN); /* ignored */
+ pqsignal(SIGPIPE, SIG_IGN); /* ignored */
+ pqsignal(SIGUSR1, handle_pm_pmsignal_signal);
+ pqsignal(SIGUSR2, dummy_handler); /* unused, reserve for children */
+ pqsignal(SIGCHLD, handle_pm_child_exit_signal);
- /*
- * Ignore SIGURG for now. Child processes may change this (see
- * InitializeLatchSupport), but they will not receive any such signals
- * until they wait on a latch.
- */
- pqsignal_pm(SIGURG, SIG_IGN); /* ignored */
-#endif
+ /* This may configure SIGURG, depending on platform. */
+ InitializeLatchSupport();
+ InitProcessLocalLatch();
/*
* No other place in Postgres should touch SIGTTIN/SIGTTOU handling. We
* child processes should just allow the inherited settings to stand.
*/
#ifdef SIGTTIN
- pqsignal_pm(SIGTTIN, SIG_IGN); /* ignored */
+ pqsignal(SIGTTIN, SIG_IGN); /* ignored */
#endif
#ifdef SIGTTOU
- pqsignal_pm(SIGTTOU, SIG_IGN); /* ignored */
+ pqsignal(SIGTTOU, SIG_IGN); /* ignored */
#endif
/* ignore SIGXFSZ, so that ulimit violations work like disk full */
#ifdef SIGXFSZ
- pqsignal_pm(SIGXFSZ, SIG_IGN); /* ignored */
+ pqsignal(SIGXFSZ, SIG_IGN); /* ignored */
#endif
+ /* Begin accepting signals. */
+ PG_SETMASK(&UnBlockSig);
+
/*
* Options setup
*/
}
}
+/*
+ * Activate or deactivate notifications of server socket events. Since we
+ * don't currently have a way to remove events from an existing WaitEventSet,
+ * we'll just destroy and recreate the whole thing. This is called during
+ * shutdown so we can wait for backends to exit without accepting new
+ * connections, and during crash reinitialization when we need to start
+ * listening for new connections again. The WaitEventSet will be freed in fork
+ * children by ClosePostmasterPorts().
+ */
+static void
+ConfigurePostmasterWaitSet(bool accept_connections)
+{
+ int nsockets;
+
+ if (pm_wait_set)
+ FreeWaitEventSet(pm_wait_set);
+ pm_wait_set = NULL;
+
+ /* How many server sockets do we need to wait for? */
+ nsockets = 0;
+ if (accept_connections)
+ {
+ while (nsockets < MAXLISTEN &&
+ ListenSocket[nsockets] != PGINVALID_SOCKET)
+ ++nsockets;
+ }
+
+ pm_wait_set = CreateWaitEventSet(CurrentMemoryContext, 1 + nsockets);
+ AddWaitEventToSet(pm_wait_set, WL_LATCH_SET, PGINVALID_SOCKET, MyLatch,
+ NULL);
+
+ if (accept_connections)
+ {
+ for (int i = 0; i < nsockets; i++)
+ AddWaitEventToSet(pm_wait_set, WL_SOCKET_ACCEPT, ListenSocket[i],
+ NULL, NULL);
+ }
+}
+
/*
* Main idle loop of postmaster
- *
- * NB: Needs to be called with signals blocked
*/
static int
ServerLoop(void)
{
- fd_set readmask;
- int nSockets;
time_t last_lockfile_recheck_time,
last_touch_time;
+ WaitEvent events[MAXLISTEN];
+ int nevents;
+ ConfigurePostmasterWaitSet(true);
last_lockfile_recheck_time = last_touch_time = time(NULL);
- nSockets = initMasks(&readmask);
-
for (;;)
{
- fd_set rmask;
- int selres;
time_t now;
+ struct timeval timeout;
- /*
- * Wait for a connection request to arrive.
- *
- * We block all signals except while sleeping. That makes it safe for
- * signal handlers, which again block all signals while executing, to
- * do nontrivial work.
- *
- * If we are in PM_WAIT_DEAD_END state, then we don't want to accept
- * any new connections, so we don't call select(), and just sleep.
- */
- memcpy((char *) &rmask, (char *) &readmask, sizeof(fd_set));
-
- if (pmState == PM_WAIT_DEAD_END)
- {
- PG_SETMASK(&UnBlockSig);
-
- pg_usleep(100000L); /* 100 msec seems reasonable */
- selres = 0;
-
- PG_SETMASK(&BlockSig);
- }
- else
- {
- /* must set timeout each time; some OSes change it! */
- struct timeval timeout;
-
- /* Needs to run with blocked signals! */
- DetermineSleepTime(&timeout);
-
- PG_SETMASK(&UnBlockSig);
-
- selres = select(nSockets, &rmask, NULL, NULL, &timeout);
+ DetermineSleepTime(&timeout);
- PG_SETMASK(&BlockSig);
- }
-
- /* Now check the select() result */
- if (selres < 0)
- {
- if (errno != EINTR && errno != EWOULDBLOCK)
- {
- ereport(LOG,
- (errcode_for_socket_access(),
- errmsg("select() failed in postmaster: %m")));
- return STATUS_ERROR;
- }
- }
+ nevents = WaitEventSetWait(pm_wait_set,
+ timeout.tv_sec * 1000 + timeout.tv_usec / 1000,
+ events,
+ lengthof(events),
+ 0 /* postmaster posts no wait_events */ );
/*
- * New connection pending on any of our sockets? If so, fork a child
- * process to deal with it.
+ * Latch set by signal handler, or new connection pending on any of
+ * our sockets? If the latter, fork a child process to deal with it.
*/
- if (selres > 0)
+ for (int i = 0; i < nevents; i++)
{
- int i;
-
- for (i = 0; i < MAXLISTEN; i++)
+ if (events[i].events & WL_LATCH_SET)
{
- if (ListenSocket[i] == PGINVALID_SOCKET)
- break;
- if (FD_ISSET(ListenSocket[i], &rmask))
+ ResetLatch(MyLatch);
+
+ /* Process work requested via signal handlers. */
+ if (pending_pm_shutdown_request)
+ process_pm_shutdown_request();
+ if (pending_pm_child_exit)
+ process_pm_child_exit();
+ if (pending_pm_reload_request)
+ process_pm_reload_request();
+ if (pending_pm_pmsignal)
+ process_pm_pmsignal();
+ }
+ else if (events[i].events & WL_SOCKET_ACCEPT)
+ {
+ Port *port;
+
+ port = ConnCreate(events[i].fd);
+ if (port)
{
- Port *port;
+ BackendStartup(port);
- port = ConnCreate(ListenSocket[i]);
- if (port)
- {
- BackendStartup(port);
-
- /*
- * We no longer need the open socket or port structure
- * in this process
- */
- StreamClose(port->sock);
- ConnFree(port);
- }
+ /*
+ * We no longer need the open socket or port structure in
+ * this process
+ */
+ StreamClose(port->sock);
+ ConnFree(port);
}
}
}
}
}
-/*
- * Initialise the masks for select() for the ports we are listening on.
- * Return the number of sockets to listen on.
- */
-static int
-initMasks(fd_set *rmask)
-{
- int maxsock = -1;
- int i;
-
- FD_ZERO(rmask);
-
- for (i = 0; i < MAXLISTEN; i++)
- {
- int fd = ListenSocket[i];
-
- if (fd == PGINVALID_SOCKET)
- break;
- FD_SET(fd, rmask);
-
- if (fd > maxsock)
- maxsock = fd;
- }
-
- return maxsock + 1;
-}
-
-
/*
* Read a client's startup packet and do something according to it.
*
{
int i;
+ /* Release resources held by the postmaster's WaitEventSet. */
+ if (pm_wait_set)
+ {
+ FreeWaitEventSetAfterFork(pm_wait_set);
+ pm_wait_set = NULL;
+ }
+
#ifndef WIN32
/*
#endif
}
+/*
+ * Child processes use SIGUSR1 to notify us of 'pmsignals'. pg_ctl uses
+ * SIGUSR1 to ask postmaster to check for logrotate and promote files.
+ */
+static void
+handle_pm_pmsignal_signal(SIGNAL_ARGS)
+{
+ int save_errno = errno;
+
+ pending_pm_pmsignal = true;
+ SetLatch(MyLatch);
+
+ errno = save_errno;
+}
/*
- * SIGHUP -- reread config files, and tell children to do same
+ * pg_ctl uses SIGHUP to request a reload of the configuration files.
*/
static void
-SIGHUP_handler(SIGNAL_ARGS)
+handle_pm_reload_request_signal(SIGNAL_ARGS)
{
int save_errno = errno;
+ pending_pm_reload_request = true;
+ SetLatch(MyLatch);
+
+ errno = save_errno;
+}
+
+/*
+ * Re-read config files, and tell children to do same.
+ */
+static void
+process_pm_reload_request(void)
+{
+ pending_pm_reload_request = false;
+
+ ereport(DEBUG2,
+ (errmsg_internal("postmaster received reload request signal")));
+
if (Shutdown <= SmartShutdown)
{
ereport(LOG,
write_nondefault_variables(PGC_SIGHUP);
#endif
}
+}
+
+/*
+ * pg_ctl uses SIGTERM, SIGINT and SIGQUIT to request different types of
+ * shutdown.
+ */
+static void
+handle_pm_shutdown_request_signal(SIGNAL_ARGS)
+{
+ int save_errno = errno;
+
+ switch (postgres_signal_arg)
+ {
+ case SIGTERM:
+ /* smart is implied if the other two flags aren't set */
+ pending_pm_shutdown_request = true;
+ break;
+ case SIGINT:
+ pending_pm_fast_shutdown_request = true;
+ pending_pm_shutdown_request = true;
+ break;
+ case SIGQUIT:
+ pending_pm_immediate_shutdown_request = true;
+ pending_pm_shutdown_request = true;
+ break;
+ }
+ SetLatch(MyLatch);
errno = save_errno;
}
-
/*
- * pmdie -- signal handler for processing various postmaster signals.
+ * Process shutdown request.
*/
static void
-pmdie(SIGNAL_ARGS)
+process_pm_shutdown_request(void)
{
- int save_errno = errno;
+ int mode;
ereport(DEBUG2,
- (errmsg_internal("postmaster received signal %d",
- postgres_signal_arg)));
+ (errmsg_internal("postmaster received shutdown request signal")));
- switch (postgres_signal_arg)
+ pending_pm_shutdown_request = false;
+
+ /*
+ * If more than one shutdown request signal arrived since the last server
+ * loop, take the one that is the most immediate. That matches the
+ * priority that would apply if we processed them one by one in any order.
+ */
+ if (pending_pm_immediate_shutdown_request)
{
- case SIGTERM:
+ pending_pm_immediate_shutdown_request = false;
+ pending_pm_fast_shutdown_request = false;
+ mode = ImmediateShutdown;
+ }
+ else if (pending_pm_fast_shutdown_request)
+ {
+ pending_pm_fast_shutdown_request = false;
+ mode = FastShutdown;
+ }
+ else
+ mode = SmartShutdown;
+
+ switch (mode)
+ {
+ case SmartShutdown:
/*
* Smart Shutdown:
PostmasterStateMachine();
break;
- case SIGINT:
+ case FastShutdown:
/*
* Fast Shutdown:
PostmasterStateMachine();
break;
- case SIGQUIT:
+ case ImmediateShutdown:
/*
* Immediate Shutdown:
PostmasterStateMachine();
break;
}
+}
+
+static void
+handle_pm_child_exit_signal(SIGNAL_ARGS)
+{
+ int save_errno = errno;
+
+ pending_pm_child_exit = true;
+ SetLatch(MyLatch);
errno = save_errno;
}
/*
- * Reaper -- signal handler to cleanup after a child process dies.
+ * Cleanup after a child process dies.
*/
static void
-reaper(SIGNAL_ARGS)
+process_pm_child_exit(void)
{
- int save_errno = errno;
int pid; /* process id of dead child process */
int exitstatus; /* its exit status */
+ pending_pm_child_exit = false;
+
ereport(DEBUG4,
(errmsg_internal("reaping dead processes")));
* or actions to make.
*/
PostmasterStateMachine();
-
- errno = save_errno;
}
/*
/*
* Advance the postmaster's state machine and take actions as appropriate
*
- * This is common code for pmdie(), reaper() and sigusr1_handler(), which
- * receive the signals that might mean we need to change state.
+ * This is common code for process_pm_shutdown_request(),
+ * process_pm_child_exit() and process_pm_pmsignal(), which process the signals
+ * that might mean we need to change state.
*/
static void
PostmasterStateMachine(void)
if (pmState == PM_WAIT_DEAD_END)
{
+ /* Don't allow any new socket connection events. */
+ ConfigurePostmasterWaitSet(false);
+
/*
* PM_WAIT_DEAD_END state ends when the BackendList is entirely empty
* (ie, no dead_end children remain), and the archiver is gone too.
pmState = PM_STARTUP;
/* crash recovery started, reset SIGKILL flag */
AbortStartTime = 0;
+
+ /* start accepting server socket connection events again */
+ ConfigurePostmasterWaitSet(true);
}
}
}
/*
- * sigusr1_handler - handle signal conditions from child processes
+ * Handle pmsignal conditions representing requests from backends,
+ * and check for promote and logrotate requests from pg_ctl.
*/
static void
-sigusr1_handler(SIGNAL_ARGS)
+process_pm_pmsignal(void)
{
- int save_errno = errno;
+ pending_pm_pmsignal = false;
+
+ ereport(DEBUG2,
+ (errmsg_internal("postmaster received pmsignal signal")));
/*
* RECOVERY_STARTED and BEGIN_HOT_STANDBY signals are ignored in
/*
* Try to advance postmaster's state machine, if a child requests it.
*
- * Be careful about the order of this action relative to sigusr1_handler's
+ * Be careful about the order of this action relative to this function's
* other actions. Generally, this should be after other actions, in case
* they have effects PostmasterStateMachine would need to know about.
* However, we should do it before the CheckPromoteSignal step, which
*/
signal_child(StartupPID, SIGUSR2);
}
-
- errno = save_errno;
}
/*