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