pgsql-v9.3-extra-daemon.v1.patch
application/octet-stream
Filename: pgsql-v9.3-extra-daemon.v1.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v9
| File | + | − |
|---|---|---|
| contrib/auth_counter/auth_counter.c | 252 | 0 |
| contrib/auth_counter/Makefile | 14 | 0 |
| contrib/Makefile | 1 | 0 |
| src/backend/bootstrap/bootstrap.c | 6 | 1 |
| src/backend/postmaster/postmaster.c | 280 | 3 |
| src/backend/storage/ipc/procsignal.c | 3 | 1 |
| src/include/bootstrap/bootstrap.h | 1 | 1 |
| src/include/postmaster/postmaster.h | 21 | 0 |
contrib/Makefile | 1 +
contrib/auth_counter/Makefile | 14 ++
contrib/auth_counter/auth_counter.c | 252 ++++++++++++++++++++++++++++++
src/backend/bootstrap/bootstrap.c | 7 +-
src/backend/postmaster/postmaster.c | 283 +++++++++++++++++++++++++++++++++-
src/backend/storage/ipc/procsignal.c | 4 +-
src/include/bootstrap/bootstrap.h | 2 +-
src/include/postmaster/postmaster.h | 21 +++
8 files changed, 578 insertions(+), 6 deletions(-)
diff --git a/contrib/Makefile b/contrib/Makefile
index d230451..74e718a 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -6,6 +6,7 @@ include $(top_builddir)/src/Makefile.global
SUBDIRS = \
adminpack \
+ auth_counter \
auth_delay \
auto_explain \
btree_gin \
diff --git a/contrib/auth_counter/Makefile b/contrib/auth_counter/Makefile
new file mode 100644
index 0000000..74ac012
--- /dev/null
+++ b/contrib/auth_counter/Makefile
@@ -0,0 +1,14 @@
+# contrib/auth_counter/Makefile
+
+MODULES = auth_counter
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/auth_counter
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/auth_counter/auth_counter.c b/contrib/auth_counter/auth_counter.c
new file mode 100644
index 0000000..d9ac177
--- /dev/null
+++ b/contrib/auth_counter/auth_counter.c
@@ -0,0 +1,252 @@
+/* -------------------------------------------------------------------------
+ *
+ * auth_counter.c
+ *
+ * Copyright (C) 2012, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * contrib/auth_delay/auth_counter.c*
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "libpq/auth.h"
+#include "libpq/pqsignal.h"
+#include "miscadmin.h"
+#include "postmaster/postmaster.h"
+#include "storage/ipc.h"
+#include "storage/lwlock.h"
+#include "storage/shmem.h"
+#include "storage/smgr.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+#include "utils/timestamp.h"
+#include <unistd.h>
+
+PG_MODULE_MAGIC;
+
+void _PG_init(void);
+
+/* GUC variable */
+static int auth_counter_interval;
+
+/* Original hooks */
+static ClientAuthentication_hook_type original_client_auth_hook;
+static shmem_startup_hook_type shmem_startup_hook_next;
+
+/* Counter value and flag of loop */
+static LWLockId auth_counter_lock;
+static long *auth_counter_success;
+static long *auth_counter_failed;
+static bool auth_counter_loop;
+
+/*
+ * auth_counter_sigterm
+ *
+ * It reset auth_counter_loop to terminate current loop.
+ */
+static void
+auth_counter_sigterm(SIGNAL_ARGS)
+{
+ auth_counter_loop = false;
+}
+
+/*
+ * auth_counter_main
+ *
+ * The main routine of this extra daemon; that logs number of successful
+ * and failed authentication for each intervals unless receiving a signal.
+ */
+static int
+auth_counter_main(const char *daemon_name)
+{
+ sigjmp_buf local_sigjmp_buf;
+ MemoryContext auth_counter_context;
+
+ /* Loop condition should be set */
+ auth_counter_loop = true;
+
+ /* Create a resource owner to keep track of our resources */
+ CurrentResourceOwner = ResourceOwnerCreate(NULL, "Auth-Counter");
+
+ /*
+ * Create a memory context that we will do all our work in. We do this so
+ * that we can reset the context during error recovery and thereby avoid
+ * possible memory leaks. Formerly this code just ran in
+ * TopMemoryContext, but resetting that would be a really bad idea.
+ */
+ auth_counter_context = AllocSetContextCreate(TopMemoryContext,
+ "Auth Counter",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ MemoryContextSwitchTo(auth_counter_context);
+
+ /*
+ * If an exception is encountered, processing resumes here.
+ *
+ * See notes in postgres.c about the design of this coding.
+ */
+ if (sigsetjmp(local_sigjmp_buf, 1) != 0)
+ {
+ /* since not using PG_TRY, must reset error stack by hand */
+ error_context_stack = NULL;
+
+ /* Prevent interrupts while cleaning up */
+ HOLD_INTERRUPTS();
+
+ /* Report the error to the server log */
+ EmitErrorReport();
+
+ /*
+ * These operations are really just a minimal subset of
+ * AbortTransaction(). We don't have very many resources to worry
+ * about in walwriter, but we do have LWLocks.
+ */
+ LWLockReleaseAll();
+
+
+ /*
+ * Now return to normal top-level context and clear ErrorContext for
+ * next time.
+ */
+ MemoryContextSwitchTo(auth_counter_context);
+ FlushErrorState();
+
+ /* Flush any leaked data in the top-level context */
+ MemoryContextResetAndDeleteChildren(auth_counter_context);
+
+ /* Now we can allow interrupts again */
+ RESUME_INTERRUPTS();
+
+ /*
+ * Sleep at least 1 second after any error. A write error is likely
+ * to be repeated, and we don't want to be filling the error logs as
+ * fast as we can.
+ */
+ pg_usleep(1000000L);
+
+ /*
+ * Close all open files after any error. This is helpful on Windows,
+ * where holding deleted files open causes various strange errors.
+ * It's not clear we need it elsewhere, but shouldn't hurt.
+ */
+ smgrcloseall();
+ }
+
+ /* We can now handle ereport(ERROR) */
+ PG_exception_stack = &local_sigjmp_buf;
+
+ /* Unblock signals (they were blocked when the postmaster forked us) */
+ PG_SETMASK(&UnBlockSig);
+
+ /*
+ * Init counter variables
+ */
+ LWLockAcquire(auth_counter_lock, LW_EXCLUSIVE);
+ *auth_counter_success = 0;
+ *auth_counter_failed = 0;
+ LWLockRelease(auth_counter_lock);
+
+ while (auth_counter_loop)
+ {
+ Datum tstamp;
+ long n_success;
+ long n_failed;
+
+ pg_usleep((long)auth_counter_interval * 1000000L);
+
+ LWLockAcquire(auth_counter_lock, LW_EXCLUSIVE);
+ n_success = *auth_counter_success;
+ n_failed = *auth_counter_failed;
+
+ *auth_counter_success = 0;
+ *auth_counter_failed = 0;
+ LWLockRelease(auth_counter_lock);
+
+ tstamp = DirectFunctionCall1(timestamptz_out,
+ TimestampTzGetDatum(GetCurrentTimestamp()));
+
+ elog(LOG, "%s(%d) %lu of login successful, %lu of failed - %s",
+ daemon_name, MyProcPid, n_success, n_failed,
+ DatumGetCString(tstamp));
+
+ /* clear temporary memory objects */
+ MemoryContextReset(auth_counter_context);
+ }
+ return 0;
+}
+
+
+/*
+ * auth_counter_check
+ *
+ * It increments the counter variable for each client authentication
+ */
+static void
+auth_counter_check(Port *port, int status)
+{
+ if (original_client_auth_hook)
+ original_client_auth_hook(port, status);
+
+ LWLockAcquire(auth_counter_lock, LW_EXCLUSIVE);
+ if (status == STATUS_OK)
+ (*auth_counter_success)++;
+ else
+ (*auth_counter_failed)++;
+ LWLockRelease(auth_counter_lock);
+}
+
+/*
+ * Callback just after shared memory allocation
+ */
+static void
+auth_counter_shmem_startup(void)
+{
+ if (shmem_startup_hook_next)
+ shmem_startup_hook_next();
+
+ /* allocate lwlock to protect counters */
+ auth_counter_lock = LWLockAssign();
+
+ /* allocate counter variable on shmem segment */
+ auth_counter_success = ShmemAlloc(sizeof(long));
+ auth_counter_failed = ShmemAlloc(sizeof(long));
+}
+
+/*
+ * Entrypoint of this module
+ */
+void
+_PG_init(void)
+{
+ DefineCustomIntVariable("auth_counter.interval",
+ "Interval to display number of logins",
+ NULL,
+ &auth_counter_interval,
+ 60, /* 1 minute (default) */
+ 5, /* 5 seconds */
+ 24 * 60 * 60, /* 1 day*/
+ PGC_SIGHUP,
+ GUC_UNIT_S,
+ NULL, NULL, NULL);
+
+ /* request for shared memory fraction */
+ RequestAddinShmemSpace(2 * sizeof(long));
+
+ shmem_startup_hook_next = shmem_startup_hook;
+ shmem_startup_hook = auth_counter_shmem_startup;
+
+ /* install a hook */
+ original_client_auth_hook = ClientAuthentication_hook;
+ ClientAuthentication_hook = auth_counter_check;
+
+ /* register an extra daemon process */
+ RegisterExtraDaemon("auth_counter",
+ EXTRA_DAEMON_NORMAL_SHUTDOWN,
+ auth_counter_main,
+ auth_counter_sigterm,
+ auth_counter_sigterm);
+}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e3ae92d..97e9e86 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -326,7 +326,9 @@ AuxiliaryProcessMain(int argc, char *argv[])
statmsg = "wal receiver process";
break;
default:
- statmsg = "??? process";
+ statmsg = GetExtraDaemonName(auxType);
+ if (!statmsg)
+ statmsg = "??? process";
break;
}
init_ps_display(statmsg, "", "", "");
@@ -436,6 +438,9 @@ AuxiliaryProcessMain(int argc, char *argv[])
proc_exit(1); /* should never return */
default:
+ /* should never return, if extra daemon correctly registered */
+ ExtraDaemonMain(auxType);
+
elog(PANIC, "unrecognized process type: %d", auxType);
proc_exit(1);
}
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index a481eef..504d417 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -323,6 +323,24 @@ static DNSServiceRef bonjour_sdref = NULL;
#endif
/*
+ * Extra daemons are auxiliary processes managed by extensions.
+ * It is available to register during shared_preload_libraries being
+ * handled (because it is only chance to load extension prior to
+ * starting up postmaster process).
+ */
+typedef struct {
+ pid_t daemon_pid;
+ int daemon_auxtype; /* >= ExtraDaemonProcess */
+ const char *daemon_name;
+ int daemon_flags;
+ ExtraDaemonMain_type daemon_main;
+ ExtraDaemonSigHup_type daemon_sighup;
+ ExtraDaemonSigTerm_type daemon_sigterm;
+} ExtraDaemon;
+
+static List *ExtraDaemonList = NIL;
+
+/*
* postmaster.c - function prototypes
*/
static void getInstallationPaths(const char *argv0);
@@ -355,6 +373,7 @@ static long PostmasterRandom(void);
static void RandomSalt(char *md5Salt);
static void signal_child(pid_t pid, int signal);
static bool SignalSomeChildren(int signal, int targets);
+static int CountActiveExtraDaemons(bool without_sync_shutdown);
#define SignalChildren(sig) SignalSomeChildren(sig, BACKEND_TYPE_ALL)
@@ -471,6 +490,7 @@ static void ShmemBackendArrayRemove(Backend *bn);
#define StartCheckpointer() StartChildProcess(CheckpointerProcess)
#define StartWalWriter() StartChildProcess(WalWriterProcess)
#define StartWalReceiver() StartChildProcess(WalReceiverProcess)
+#define StartExtraDaemon(ed) StartChildProcess((ed)->daemon_auxtype)
/* Macros to check exit status of a child process */
#define EXIT_STATUS_0(st) ((st) == 0)
@@ -1414,6 +1434,20 @@ ServerLoop(void)
if (PgStatPID == 0 && pmState == PM_RUN)
PgStatPID = pgstat_start();
+ /* If we have lost extra daemons, try to start a new one */
+ if (pmState == PM_RUN)
+ {
+ ListCell *cell;
+
+ foreach (cell, ExtraDaemonList)
+ {
+ ExtraDaemon *daemon = lfirst(cell);
+
+ if (daemon->daemon_pid == 0)
+ daemon->daemon_pid = StartExtraDaemon(daemon);
+ }
+ }
+
/* If we need to signal the autovacuum launcher, do so now */
if (avlauncher_needs_signal)
{
@@ -2047,6 +2081,7 @@ static void
SIGHUP_handler(SIGNAL_ARGS)
{
int save_errno = errno;
+ ListCell *cell;
PG_SETMASK(&BlockSig);
@@ -2074,6 +2109,13 @@ SIGHUP_handler(SIGNAL_ARGS)
signal_child(SysLoggerPID, SIGHUP);
if (PgStatPID != 0)
signal_child(PgStatPID, SIGHUP);
+ foreach (cell, ExtraDaemonList)
+ {
+ ExtraDaemon *daemon = lfirst(cell);
+
+ if (daemon->daemon_pid != 0)
+ signal_child(daemon->daemon_pid, SIGHUP);
+ }
/* Reload authentication config files too */
if (!load_hba())
@@ -2101,6 +2143,7 @@ static void
pmdie(SIGNAL_ARGS)
{
int save_errno = errno;
+ ListCell *cell;
PG_SETMASK(&BlockSig);
@@ -2136,6 +2179,15 @@ pmdie(SIGNAL_ARGS)
signal_child(WalWriterPID, SIGTERM);
if (BgWriterPID != 0)
signal_child(BgWriterPID, SIGTERM);
+ /* and the extra daemons too */
+ foreach (cell, ExtraDaemonList)
+ {
+ ExtraDaemon *daemon = lfirst(cell);
+
+ if (daemon->daemon_pid != 0 &&
+ (daemon->daemon_flags & EXTRA_DAEMON_NORMAL_SHUTDOWN))
+ signal_child(daemon->daemon_pid, SIGTERM);
+ }
/*
* If we're in recovery, we can't kill the startup process
@@ -2200,6 +2252,15 @@ pmdie(SIGNAL_ARGS)
/* and the walwriter too */
if (WalWriterPID != 0)
signal_child(WalWriterPID, SIGTERM);
+ /* and the extra daemons too */
+ foreach (cell, ExtraDaemonList)
+ {
+ ExtraDaemon *daemon = lfirst(cell);
+
+ if (daemon->daemon_pid != 0 &&
+ (daemon->daemon_flags & EXTRA_DAEMON_NORMAL_SHUTDOWN))
+ signal_child(daemon->daemon_pid, SIGTERM);
+ }
pmState = PM_WAIT_BACKENDS;
}
@@ -2237,6 +2298,13 @@ pmdie(SIGNAL_ARGS)
signal_child(PgArchPID, SIGQUIT);
if (PgStatPID != 0)
signal_child(PgStatPID, SIGQUIT);
+ foreach (cell, ExtraDaemonList)
+ {
+ ExtraDaemon *daemon = lfirst(cell);
+
+ if (daemon->daemon_pid != 0)
+ signal_child(daemon->daemon_pid, SIGQUIT);
+ }
ExitPostmaster(0);
break;
}
@@ -2252,6 +2320,7 @@ pmdie(SIGNAL_ARGS)
static void
reaper(SIGNAL_ARGS)
{
+ ListCell *cell;
int save_errno = errno;
int pid; /* process id of dead child process */
int exitstatus; /* its exit status */
@@ -2383,6 +2452,13 @@ reaper(SIGNAL_ARGS)
PgArchPID = pgarch_start();
if (PgStatPID == 0)
PgStatPID = pgstat_start();
+ foreach (cell, ExtraDaemonList)
+ {
+ ExtraDaemon *daemon = lfirst(cell);
+
+ if (daemon->daemon_pid == 0)
+ daemon->daemon_pid = StartExtraDaemon(daemon);
+ }
/* at this point we are really open for business */
ereport(LOG,
@@ -2548,6 +2624,24 @@ reaper(SIGNAL_ARGS)
continue;
}
+ /* Was it an extra daemon? If so, try to start a new one */
+ foreach (cell, ExtraDaemonList)
+ {
+ ExtraDaemon *daemon = lfirst(cell);
+
+ if (pid == daemon->daemon_pid)
+ {
+ daemon->daemon_pid = 0;
+ if (!EXIT_STATUS_0(exitstatus))
+ LogChildExit(LOG, daemon->daemon_name, pid, exitstatus);
+ if (pmState == PM_RUN)
+ daemon->daemon_pid = StartExtraDaemon(daemon);
+ break;
+ }
+ }
+ if (cell != NULL)
+ continue;
+
/*
* Else do standard backend child cleanup.
*/
@@ -2649,6 +2743,7 @@ HandleChildCrash(int pid, int exitstatus, const char *procname)
Dlelem *curr,
*next;
Backend *bp;
+ ListCell *cell;
/*
* Make log entry unless there was a previous crash (if so, nonzero exit
@@ -2810,6 +2905,23 @@ HandleChildCrash(int pid, int exitstatus, const char *procname)
allow_immediate_pgstat_restart();
}
+ /* Take care of the extra daemons too */
+ foreach (cell, ExtraDaemonList)
+ {
+ ExtraDaemon *daemon = lfirst(cell);
+
+ if (pid == daemon->daemon_pid)
+ daemon->daemon_pid = 0;
+ else if (daemon->daemon_pid != 0 && !FatalError)
+ {
+ ereport(DEBUG2,
+ (errmsg_internal("sending %s to process %d (%s)",
+ "SIGQUIT",
+ (int) daemon->daemon_pid,
+ daemon->daemon_name)));
+ signal_child(daemon->daemon_pid, SIGQUIT);
+ }
+ }
/* We do NOT restart the syslogger */
FatalError = true;
@@ -2954,7 +3066,8 @@ PostmasterStateMachine(void)
BgWriterPID == 0 &&
(CheckpointerPID == 0 || !FatalError) &&
WalWriterPID == 0 &&
- AutoVacPID == 0)
+ AutoVacPID == 0 &&
+ CountActiveExtraDaemons(true) == 0)
{
if (FatalError)
{
@@ -2988,6 +3101,8 @@ PostmasterStateMachine(void)
}
else
{
+ ListCell *cell;
+
/*
* If we failed to fork a checkpointer, just shut down. Any
* required cleanup will happen at next restart. We set
@@ -3003,6 +3118,13 @@ PostmasterStateMachine(void)
signal_child(PgArchPID, SIGQUIT);
if (PgStatPID != 0)
signal_child(PgStatPID, SIGQUIT);
+ foreach(cell, ExtraDaemonList)
+ {
+ ExtraDaemon *daemon = lfirst(cell);
+
+ if (daemon->daemon_pid != 0)
+ signal_child(daemon->daemon_pid, SIGQUIT);
+ }
}
}
}
@@ -3020,7 +3142,7 @@ PostmasterStateMachine(void)
* shutdown is performed during recovery.
*/
if (PgArchPID == 0 && CountChildren(BACKEND_TYPE_ALL) == 0 &&
- WalReceiverPID == 0)
+ WalReceiverPID == 0 && CountActiveExtraDaemons(false) == 0)
{
pmState = PM_WAIT_DEAD_END;
}
@@ -4535,7 +4657,7 @@ StartChildProcess(AuxProcType type)
break;
default:
ereport(LOG,
- (errmsg("could not fork process: %m")));
+ (errmsg("could not fork extra daemon process: %m")));
break;
}
@@ -5156,3 +5278,158 @@ InitPostmasterDeathWatchHandle(void)
GetLastError())));
#endif /* WIN32 */
}
+
+/*
+ * RegisterExtraDaemon
+ *
+ * It registers an extra daemon that shall perform under the postmaster
+ * process. Due to technical reason, it has to be registered during
+ * shared_preload_libraries being handled.
+ */
+void
+RegisterExtraDaemon(const char *daemon_name,
+ int daemon_flags,
+ ExtraDaemonMain_type daemon_main,
+ ExtraDaemonSigHup_type daemon_sighup,
+ ExtraDaemonSigTerm_type daemon_sigterm)
+{
+ ExtraDaemon *daemon;
+ MemoryContext oldcxt;
+ static int auxType_last = ExtraDaemonProcess;
+
+ if (!process_shared_preload_libraries_in_progress)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("extra daemon must be registered during shared_preload_libraries being handled")));
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+
+ daemon = palloc(sizeof(ExtraDaemon));
+ daemon->daemon_pid = 0;
+ daemon->daemon_name = pstrdup(daemon_name);
+ daemon->daemon_flags = daemon_flags;
+ daemon->daemon_main = daemon_main;
+ daemon->daemon_sighup = daemon_sighup;
+ daemon->daemon_sigterm = daemon_sigterm;
+ daemon->daemon_auxtype = auxType_last++;
+
+ ExtraDaemonList = lappend(ExtraDaemonList, daemon);
+
+ MemoryContextSwitchTo(oldcxt);
+}
+
+static void
+ExtraDaemonQuickDie(SIGNAL_ARGS)
+{
+ PG_SETMASK(&BlockSig);
+
+ /*
+ * We DO NOT want to run proc_exit() callbacks -- we're here because
+ * shared memory may be corrupted, so we don't want to try to clean up our
+ * transaction. Just nail the windows shut and get out of town. Now that
+ * there's an atexit callback to prevent third-party code from breaking
+ * things by calling exit() directly, we have to reset the callbacks
+ * explicitly to make this work as intended.
+ */
+ on_exit_reset();
+
+ /*
+ * Note we do exit(2) not exit(0). This is to force the postmaster into a
+ * system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
+ * backend. This is necessary precisely because we don't clean up our
+ * shared memory state. (The "dead man switch" mechanism in pmsignal.c
+ * should ensure the postmaster sees this as a crash, too, but no harm in
+ * being doubly sure.)
+ */
+ exit(2);
+}
+
+void
+ExtraDaemonMain(int auxType)
+{
+ ListCell *cell;
+
+ foreach(cell, ExtraDaemonList)
+ {
+ ExtraDaemon *daemon = lfirst(cell);
+
+ if (daemon->daemon_auxtype != auxType)
+ continue;
+
+ /* If possible, make this process a group leader. */
+#ifdef HAVE_SETSID
+ if (setsid() < 0)
+ elog(FATAL, "setsid() failed: %m");
+#endif
+ /*
+ * Fixup signal handlers. Please note that signals are still
+ * masked at this moment, so extension has to unmask them
+ * once it become ready to accept signals; using PG_SETMASK()
+ */
+ pqsignal(SIGHUP, daemon->daemon_sighup);
+ pqsignal(SIGINT, SIG_IGN);
+ pqsignal(SIGTERM, daemon->daemon_sigterm);
+ pqsignal(SIGQUIT, ExtraDaemonQuickDie);
+ pqsignal(SIGALRM, SIG_IGN);
+ pqsignal(SIGPIPE, SIG_IGN);
+ pqsignal(SIGUSR1, SIG_IGN);
+ pqsignal(SIGUSR2, SIG_IGN);
+
+ pqsignal(SIGCHLD, SIG_DFL);
+ pqsignal(SIGTTIN, SIG_DFL);
+ pqsignal(SIGTTOU, SIG_DFL);
+ pqsignal(SIGCONT, SIG_DFL);
+ pqsignal(SIGWINCH, SIG_DFL);
+
+ /* We allow SIGQUIT (quickdie) at all times */
+ sigdelset(&BlockSig, SIGQUIT);
+
+ /* Launch extension code */
+ proc_exit(daemon->daemon_main(daemon->daemon_name));
+ }
+}
+
+const char *
+GetExtraDaemonName(int auxType)
+{
+ ListCell *cell;
+ const char *result = NULL;
+
+ foreach (cell, ExtraDaemonList)
+ {
+ ExtraDaemon *daemon = lfirst(cell);
+
+ if (daemon->daemon_auxtype == auxType)
+ {
+ result = daemon->daemon_name;
+ break;
+ }
+ }
+ return result;
+}
+
+int
+GetNumExtraDaemons(void)
+{
+ return list_length(ExtraDaemonList);
+}
+
+static int
+CountActiveExtraDaemons(bool without_sync_shutdown)
+{
+ ListCell *cell;
+ int result = 0;
+
+ foreach (cell, ExtraDaemonList)
+ {
+ ExtraDaemon *daemon = lfirst(cell);
+
+ if (without_sync_shutdown &&
+ (daemon->daemon_flags & EXTRA_DAEMON_NORMAL_SHUTDOWN))
+ continue;
+
+ if (daemon->daemon_pid != 0)
+ result++;
+ }
+ return result;
+}
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 3d7e85f..3835e0f 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -20,6 +20,7 @@
#include "bootstrap/bootstrap.h"
#include "commands/async.h"
#include "miscadmin.h"
+#include "postmaster/postmaster.h"
#include "storage/latch.h"
#include "storage/ipc.h"
#include "storage/sinval.h"
@@ -55,7 +56,8 @@ typedef struct
* possible auxiliary process type. (This scheme assumes there is not
* more than one of any auxiliary process type at a time.)
*/
-#define NumProcSignalSlots (MaxBackends + NUM_AUXPROCTYPES)
+#define NumProcSignalSlots \
+ (MaxBackends + ExtraDaemonProcess + GetNumExtraDaemons())
static ProcSignalSlot *ProcSignalSlots = NULL;
static volatile ProcSignalSlot *MyProcSignalSlot = NULL;
diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h
index e966a73..09f9148 100644
--- a/src/include/bootstrap/bootstrap.h
+++ b/src/include/bootstrap/bootstrap.h
@@ -26,7 +26,7 @@ typedef enum
WalWriterProcess,
WalReceiverProcess,
- NUM_AUXPROCTYPES /* Must be last! */
+ ExtraDaemonProcess /* Must be last! */
} AuxProcType;
/*
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index dded0e6..ac53737 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -57,4 +57,25 @@ extern Size ShmemBackendArraySize(void);
extern void ShmemBackendArrayAllocation(void);
#endif
+/*
+ * Set this flag, if extra daemon want to start shutdown process concurrently
+ * with other backend processes (including normal database backend).
+ * If not set, the extra daemon will receive SIGQUIT after all the backend
+ * process get terminated.
+ */
+#define EXTRA_DAEMON_NORMAL_SHUTDOWN 0x0001
+
+typedef int (*ExtraDaemonMain_type)(const char *name);
+typedef void (*ExtraDaemonSigHup_type)(SIGNAL_ARGS);
+typedef void (*ExtraDaemonSigTerm_type)(SIGNAL_ARGS);
+
+extern void RegisterExtraDaemon(const char *daemon_name,
+ int daemon_flags,
+ ExtraDaemonMain_type daemon_main,
+ ExtraDaemonSigHup_type daemon_sighup,
+ ExtraDaemonSigTerm_type daemon_sigterm);
+extern void ExtraDaemonMain(int auxType);
+extern const char *GetExtraDaemonName(int auxType);
+extern int GetNumExtraDaemons(void);
+
#endif /* _POSTMASTER_H */