 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 */
