*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.254 2001/11/02 18:39:57 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.255 2001/11/04 19:55:31 tgl Exp $
*
* NOTES
*
#include "nodes/nodes.h"
#include "storage/fd.h"
#include "storage/ipc.h"
+#include "storage/pmsignal.h"
#include "storage/proc.h"
#include "access/xlog.h"
#include "tcop/tcopprot.h"
static char *progname = (char *) NULL;
-/* flag to indicate that SIGHUP arrived during server loop */
-static volatile bool got_SIGHUP = false;
-
/*
* Default Values
*/
static void SIGHUP_handler(SIGNAL_ARGS);
static void pmdie(SIGNAL_ARGS);
static void reaper(SIGNAL_ARGS);
-static void schedule_checkpoint(SIGNAL_ARGS);
+static void sigusr1_handler(SIGNAL_ARGS);
+static void dummy_handler(SIGNAL_ARGS);
static void CleanupProc(int pid, int exitstatus);
static int DoBackend(Port *port);
static void ExitPostmaster(int status);
pqsignal(SIGTERM, pmdie); /* wait for children and ShutdownDataBase */
pqsignal(SIGALRM, SIG_IGN); /* ignored */
pqsignal(SIGPIPE, SIG_IGN); /* ignored */
- pqsignal(SIGUSR1, schedule_checkpoint); /* start a background
- * checkpoint */
- pqsignal(SIGUSR2, pmdie); /* send SIGUSR2, don't die */
+ pqsignal(SIGUSR1, sigusr1_handler); /* message from child process */
+ pqsignal(SIGUSR2, dummy_handler); /* unused, reserve for children */
pqsignal(SIGCHLD, reaper); /* handle child termination */
pqsignal(SIGTTIN, SIG_IGN); /* ignored */
pqsignal(SIGTTOU, SIG_IGN); /* ignored */
Port *port;
fd_set rmask,
wmask;
- struct timeval *timeout = NULL;
- struct timeval timeout_tv;
+ struct timeval timeout;
+
+ /*
+ * The timeout for the select() below is normally set on the basis
+ * of the time to the next checkpoint. However, if for some reason
+ * we don't have a next-checkpoint time, time out after 60 seconds.
+ * This keeps checkpoint scheduling from locking up when we get new
+ * connection requests infrequently (since we are likely to detect
+ * checkpoint completion just after enabling signals below, after
+ * we've already made the decision about how long to wait this time).
+ */
+ timeout.tv_sec = 60;
+ timeout.tv_usec = 0;
if (CheckPointPID == 0 && checkpointed &&
Shutdown == NoShutdown && !FatalError && random_seed != 0)
if (CheckPointTimeout + checkpointed > now)
{
/*
- * Not time for checkpoint yet, so set a timeout for
- * select
+ * Not time for checkpoint yet, so set select timeout
*/
- timeout_tv.tv_sec = CheckPointTimeout + checkpointed - now;
- timeout_tv.tv_usec = 0;
- timeout = &timeout_tv;
+ timeout.tv_sec = CheckPointTimeout + checkpointed - now;
}
else
{
* delay
*/
if (CheckPointPID == 0)
- checkpointed = now - (9 * CheckPointTimeout) / 10;
+ {
+ timeout.tv_sec = CheckPointTimeout / 10;
+ checkpointed = now + timeout.tv_sec - CheckPointTimeout;
+ }
}
}
PG_SETMASK(&UnBlockSig);
- if (select(nSockets, &rmask, &wmask, (fd_set *) NULL, timeout) < 0)
+ if (select(nSockets, &rmask, &wmask, (fd_set *) NULL, &timeout) < 0)
{
PG_SETMASK(&BlockSig);
if (errno == EINTR || errno == EWOULDBLOCK)
continue;
- elog(DEBUG, "ServerLoop: select failed: %s", strerror(errno));
+ elog(DEBUG, "ServerLoop: select failed: %m");
return STATUS_ERROR;
}
/*
- * Block all signals until we wait again
+ * Block all signals until we wait again. (This makes it safe
+ * for our signal handlers to do nontrivial work.)
*/
PG_SETMASK(&BlockSig);
/*
- * Respond to signals, if needed
- */
- if (got_SIGHUP)
- {
- got_SIGHUP = false;
- ProcessConfigFile(PGC_SIGHUP);
- load_hba_and_ident();
- load_password_cache();
- }
-
- /*
* Select a random seed at the time of first receiving a request.
*/
while (random_seed == 0)
if (Shutdown <= SmartShutdown)
{
- got_SIGHUP = true;
SignalChildren(SIGHUP);
+ ProcessConfigFile(PGC_SIGHUP);
+ load_hba_and_ident();
}
+ PG_SETMASK(&UnBlockSig);
+
errno = save_errno;
}
switch (postgres_signal_arg)
{
- case SIGUSR2:
-
- /*
- * Send SIGUSR2 to all children (AsyncNotifyHandler)
- */
- if (Shutdown > SmartShutdown)
- {
- errno = save_errno;
- return;
- }
- SignalChildren(SIGUSR2);
- errno = save_errno;
- return;
-
case SIGTERM:
/*
* Wait for children to end their work and ShutdownDataBase.
*/
if (Shutdown >= SmartShutdown)
- {
- errno = save_errno;
- return;
- }
+ break;
Shutdown = SmartShutdown;
elog(DEBUG, "smart shutdown request");
if (DLGetHead(BackendList)) /* let reaper() handle this */
- {
- errno = save_errno;
- return;
- }
+ break;
/*
* No children left. Shutdown data base system.
*/
if (StartupPID > 0 || FatalError) /* let reaper() handle
* this */
- {
- errno = save_errno;
- return;
- }
+ break;
if (ShutdownPID > 0)
{
elog(REALLYFATAL, "shutdown process %d already running",
}
ShutdownPID = ShutdownDataBase();
- errno = save_errno;
- return;
+ break;
case SIGINT:
* and exit) and ShutdownDataBase when they are gone.
*/
if (Shutdown >= FastShutdown)
- {
- errno = save_errno;
- return;
- }
+ break;
elog(DEBUG, "fast shutdown request");
if (DLGetHead(BackendList)) /* let reaper() handle this */
{
elog(DEBUG, "aborting any active transactions");
SignalChildren(SIGTERM);
}
- errno = save_errno;
- return;
+ break;
}
if (Shutdown > NoShutdown)
{
Shutdown = FastShutdown;
- errno = save_errno;
- return;
+ break;
}
Shutdown = FastShutdown;
*/
if (StartupPID > 0 || FatalError) /* let reaper() handle
* this */
- {
- errno = save_errno;
- return;
- }
+ break;
if (ShutdownPID > 0)
{
elog(REALLYFATAL, "shutdown process %d already running",
}
ShutdownPID = ShutdownDataBase();
- errno = save_errno;
- return;
+ break;
case SIGQUIT:
kill(StartupPID, SIGQUIT);
if (DLGetHead(BackendList))
SignalChildren(SIGQUIT);
+ ExitPostmaster(0);
break;
}
- /* exit postmaster */
- ExitPostmaster(0);
+ PG_SETMASK(&UnBlockSig);
+
+ errno = save_errno;
}
/*
reaper(SIGNAL_ARGS)
{
int save_errno = errno;
-
#ifdef HAVE_WAITPID
int status; /* backend exit status */
-
#else
union wait status; /* backend exit status */
#endif
int pid; /* process id of dead backend */
PG_SETMASK(&BlockSig);
- /* It's not really necessary to reset the handler each time is it? */
- pqsignal(SIGCHLD, reaper);
if (DebugLvl)
elog(DEBUG, "reaping dead processes");
CheckPointPID = 0;
checkpointed = time(NULL);
- errno = save_errno;
- return;
+ goto reaper_done;
}
CleanupProc(pid, exitstatus);
* StartupDataBase.
*/
if (DLGetHead(BackendList) || StartupPID > 0 || ShutdownPID > 0)
- {
- errno = save_errno;
- return;
- }
+ goto reaper_done;
elog(DEBUG, "all server processes terminated; reinitializing shared memory and semaphores");
shmem_exit(0);
reset_shared(PostPortNumber);
StartupPID = StartupDataBase();
- errno = save_errno;
- return;
+
+ goto reaper_done;
}
if (Shutdown > NoShutdown)
{
if (DLGetHead(BackendList))
- {
- errno = save_errno;
- return;
- }
+ goto reaper_done;
if (StartupPID > 0 || ShutdownPID > 0)
- {
- errno = save_errno;
- return;
- }
+ goto reaper_done;
ShutdownPID = ShutdownDataBase();
}
+reaper_done:
+ PG_SETMASK(&UnBlockSig);
+
errno = save_errno;
}
proc_exit(status);
}
-/* Request to schedule a checkpoint (no-op if one is currently running) */
+/*
+ * sigusr1_handler - handle signal conditions from child processes
+ */
static void
-schedule_checkpoint(SIGNAL_ARGS)
+sigusr1_handler(SIGNAL_ARGS)
{
int save_errno = errno;
PG_SETMASK(&BlockSig);
- /* Ignore request if checkpointing is currently disabled */
- if (CheckPointPID == 0 && checkpointed &&
- Shutdown == NoShutdown && !FatalError && random_seed != 0)
+ if (CheckPostmasterSignal(PMSIGNAL_DO_CHECKPOINT))
{
- CheckPointPID = CheckPointDataBase();
- /* note: if fork fails, CheckPointPID stays 0; nothing happens */
+ /*
+ * Request to schedule a checkpoint
+ *
+ * Ignore request if checkpoint is already running or
+ * checkpointing is currently disabled
+ */
+ if (CheckPointPID == 0 && checkpointed &&
+ Shutdown == NoShutdown && !FatalError && random_seed != 0)
+ {
+ CheckPointPID = CheckPointDataBase();
+ /* note: if fork fails, CheckPointPID stays 0; nothing happens */
+ }
+ }
+
+ if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE))
+ {
+ /*
+ * Password file has changed.
+ */
+ load_password_cache();
}
+ if (CheckPostmasterSignal(PMSIGNAL_WAKEN_CHILDREN))
+ {
+ /*
+ * Send SIGUSR2 to all children (triggers AsyncNotifyHandler).
+ * See storage/ipc/sinvaladt.c for the use of this.
+ */
+ if (Shutdown == NoShutdown)
+ SignalChildren(SIGUSR2);
+ }
+
+ PG_SETMASK(&UnBlockSig);
+
errno = save_errno;
}
/*
+ * Dummy signal handler
+ *
+ * We use this for signals that we don't actually use in the postmaster,
+ * but we do use in backends. If we SIG_IGN such signals in the postmaster,
+ * then a newly started backend might drop a signal that arrives before it's
+ * able to reconfigure its signal processing. (See notes in postgres.c.)
+ */
+static void
+dummy_handler(SIGNAL_ARGS)
+{
+}
+
+
+/*
* CharRemap: given an int in range 0..61, produce textual encoding of it
* per crypt(3) conventions.
*/
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * pmsignal.c
+ * routines for signaling the postmaster from its child processes
+ *
+ *
+ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/storage/ipc/pmsignal.c,v 1.1 2001/11/04 19:55:31 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <signal.h>
+#include <unistd.h>
+
+#include "miscadmin.h"
+#include "storage/pmsignal.h"
+#include "storage/shmem.h"
+
+
+/*
+ * The postmaster is signaled by its children by sending SIGUSR1. The
+ * specific reason is communicated via flags in shared memory. We keep
+ * a boolean flag for each possible "reason", so that different reasons
+ * can be signaled by different backends at the same time. (However,
+ * if the same reason is signaled more than once simultaneously, the
+ * postmaster will observe it only once.)
+ *
+ * The flags are actually declared as "volatile sig_atomic_t" for maximum
+ * portability. This should ensure that loads and stores of the flag
+ * values are atomic, allowing us to dispense with any explicit locking.
+ */
+
+static volatile sig_atomic_t * PMSignalFlags;
+
+
+/*
+ * PMSignalInit - initialize during shared-memory creation
+ */
+void
+PMSignalInit(void)
+{
+ /* Should be called only once */
+ Assert(!PMSignalFlags);
+
+ PMSignalFlags = (sig_atomic_t *)
+ ShmemAlloc(NUM_PMSIGNALS * sizeof(sig_atomic_t));
+
+ MemSet(PMSignalFlags, 0, NUM_PMSIGNALS * sizeof(sig_atomic_t));
+}
+
+/*
+ * SendPostmasterSignal - signal the postmaster from a child process
+ */
+void
+SendPostmasterSignal(PMSignalReason reason)
+{
+ /* If called in a standalone backend, do nothing */
+ if (!IsUnderPostmaster)
+ return;
+ /* Atomically set the proper flag */
+ PMSignalFlags[reason] = true;
+ /* Send signal to postmaster (assume it is our direct parent) */
+ kill(getppid(), SIGUSR1);
+}
+
+/*
+ * CheckPostmasterSignal - check to see if a particular reason has been
+ * signaled, and clear the signal flag. Should be called by postmaster
+ * after receiving SIGUSR1.
+ */
+bool
+CheckPostmasterSignal(PMSignalReason reason)
+{
+ /* Careful here --- don't clear flag if we haven't seen it set */
+ if (PMSignalFlags[reason])
+ {
+ PMSignalFlags[reason] = false;
+ return true;
+ }
+ return false;
+}