/*------------------------------------------------------------------------- * * pqsignal.c * reliable BSD-style signal(2) routine stolen from RWW who stole it * from Stevens... * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/libpq/pqsignal.c,v 1.32 2004/02/18 16:25:12 momjian Exp $ * * NOTES * This shouldn't be in libpq, but the monitor and some other * things need it... * * A NOTE ABOUT SIGNAL HANDLING ACROSS THE VARIOUS PLATFORMS. * * pg_config.h defines the macro HAVE_POSIX_SIGNALS for some platforms and * not for others. This file and pqsignal.h use that macro to decide * how to handle signalling. * * signal(2) handling - this is here because it affects some of * the frontend commands as well as the backend server. * * Ultrix and SunOS provide BSD signal(2) semantics by default. * * SVID2 and POSIX signal(2) semantics differ from BSD signal(2) * semantics. We can use the POSIX sigaction(2) on systems that * allow us to request restartable signals (SA_RESTART). * * Some systems don't allow restartable signals at all unless we * link to a special BSD library. * * We devoutly hope that there aren't any systems that provide * neither POSIX signals nor BSD signals. The alternative * is to do signal-handler reinstallation, which doesn't work well * at all. * ------------------------------------------------------------------------*/ #ifdef WIN32 #define _WIN32_WINNT 0x0400 #endif #include "postgres.h" #include #include "libpq/pqsignal.h" /* * Initialize BlockSig, UnBlockSig, and AuthBlockSig. * * BlockSig is the set of signals to block when we are trying to block * signals. This includes all signals we normally expect to get, but NOT * signals that should never be turned off. * * AuthBlockSig is the set of signals to block during authentication; * it's essentially BlockSig minus SIGTERM, SIGQUIT, SIGALRM. * * UnBlockSig is the set of signals to block when we don't want to block * signals (is this ever nonzero??) */ void pqinitmask(void) { #ifdef HAVE_SIGPROCMASK sigemptyset(&UnBlockSig); sigfillset(&BlockSig); sigfillset(&AuthBlockSig); /* * Unmark those signals that should never be blocked. Some of these * signal names don't exist on all platforms. Most do, but might as * well ifdef them all for consistency... */ #ifdef SIGTRAP sigdelset(&BlockSig, SIGTRAP); sigdelset(&AuthBlockSig, SIGTRAP); #endif #ifdef SIGABRT sigdelset(&BlockSig, SIGABRT); sigdelset(&AuthBlockSig, SIGABRT); #endif #ifdef SIGILL sigdelset(&BlockSig, SIGILL); sigdelset(&AuthBlockSig, SIGILL); #endif #ifdef SIGFPE sigdelset(&BlockSig, SIGFPE); sigdelset(&AuthBlockSig, SIGFPE); #endif #ifdef SIGSEGV sigdelset(&BlockSig, SIGSEGV); sigdelset(&AuthBlockSig, SIGSEGV); #endif #ifdef SIGBUS sigdelset(&BlockSig, SIGBUS); sigdelset(&AuthBlockSig, SIGBUS); #endif #ifdef SIGSYS sigdelset(&BlockSig, SIGSYS); sigdelset(&AuthBlockSig, SIGSYS); #endif #ifdef SIGCONT sigdelset(&BlockSig, SIGCONT); sigdelset(&AuthBlockSig, SIGCONT); #endif #ifdef SIGTERM sigdelset(&AuthBlockSig, SIGTERM); #endif #ifdef SIGQUIT sigdelset(&AuthBlockSig, SIGQUIT); #endif #ifdef SIGALRM sigdelset(&AuthBlockSig, SIGALRM); #endif #else UnBlockSig = 0; BlockSig = sigmask(SIGHUP) | sigmask(SIGQUIT) | sigmask(SIGTERM) | sigmask(SIGALRM) | sigmask(SIGINT) | sigmask(SIGUSR1) | sigmask(SIGUSR2) | sigmask(SIGCHLD) | sigmask(SIGWINCH) | sigmask(SIGFPE); AuthBlockSig = sigmask(SIGHUP) | sigmask(SIGINT) | sigmask(SIGUSR1) | sigmask(SIGUSR2) | sigmask(SIGCHLD) | sigmask(SIGWINCH) | sigmask(SIGFPE); #endif } #ifndef WIN32 /* * Set up a signal handler */ pqsigfunc pqsignal(int signo, pqsigfunc func) { #if !defined(HAVE_POSIX_SIGNALS) return signal(signo, func); #else struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (signo != SIGALRM) act.sa_flags |= SA_RESTART; if (sigaction(signo, &act, &oact) < 0) return SIG_ERR; return oact.sa_handler; #endif /* !HAVE_POSIX_SIGNALS */ } #else /* Win32 specific signals code */ /* pg_signal_crit_sec is used to protect only pg_signal_queue. That is the only * variable that can be accessed from the signal sending threads! */ static CRITICAL_SECTION pg_signal_crit_sec; static int pg_signal_queue; #define PG_SIGNAL_COUNT 32 static pqsigfunc pg_signal_array[PG_SIGNAL_COUNT]; static pqsigfunc pg_signal_defaults[PG_SIGNAL_COUNT]; static int pg_signal_mask; HANDLE pgwin32_main_thread_handle; /* Signal handling thread function */ static DWORD WINAPI pg_signal_thread(LPVOID param); static BOOL WINAPI pg_console_handler(DWORD dwCtrlType); /* Initialization */ void pgwin32_signal_initialize(void) { int i; HANDLE signal_thread_handle; InitializeCriticalSection(&pg_signal_crit_sec); for (i = 0; i < PG_SIGNAL_COUNT; i++) { pg_signal_array[i] = SIG_DFL; pg_signal_defaults[i] = SIG_IGN; } pg_signal_mask = 0; pg_signal_queue = 0; /* Get handle to main thread so we can post calls to it later */ if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &pgwin32_main_thread_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) ereport(FATAL, (errmsg_internal("Failed to get main thread handle!"))); /* Create thread for handling signals */ signal_thread_handle = CreateThread(NULL, 0, pg_signal_thread, NULL, 0, NULL); if (signal_thread_handle == NULL) ereport(FATAL, (errmsg_internal("Failed to create signal handler thread!"))); if (!SetConsoleCtrlHandler(pg_console_handler, TRUE)) ereport(FATAL, (errmsg_internal("Failed to set console control handler!"))); } /* Dispatch all signals currently queued and not blocked * Blocked signals are ignored, and will be fired at the time of * the sigsetmask() call. */ static void dispatch_queued_signals(void) { int i; EnterCriticalSection(&pg_signal_crit_sec); while (pg_signal_queue & ~pg_signal_mask) { /* One or more unblocked signals queued for execution */ int exec_mask = pg_signal_queue & ~pg_signal_mask; for (i = 0; i < PG_SIGNAL_COUNT; i++) { if (exec_mask & sigmask(i)) { /* Execute this signal */ pqsigfunc sig = pg_signal_array[i]; if (sig == SIG_DFL) sig = pg_signal_defaults[i]; pg_signal_queue &= ~sigmask(i); if (sig != SIG_ERR && sig != SIG_IGN && sig != SIG_DFL) { LeaveCriticalSection(&pg_signal_crit_sec); sig(i); EnterCriticalSection(&pg_signal_crit_sec); break; /* Restart outer loop, in case signal mask * or queue has been modified inside * signal handler */ } } } } LeaveCriticalSection(&pg_signal_crit_sec); } /* signal masking. Only called on main thread, no sync required */ int pqsigsetmask(int mask) { int prevmask; prevmask = pg_signal_mask; pg_signal_mask = mask; /* * Dispatch any signals queued up right away, in case we have * unblocked one or more signals previously queued */ dispatch_queued_signals(); return prevmask; } /* signal manipulation. Only called on main thread, no sync required */ pqsigfunc pqsignal(int signum, pqsigfunc handler) { pqsigfunc prevfunc; if (signum >= PG_SIGNAL_COUNT || signum < 0) return SIG_ERR; prevfunc = pg_signal_array[signum]; pg_signal_array[signum] = handler; return prevfunc; } /* signal sending */ int pqkill(int pid, int sig) { char pipename[128]; BYTE sigData = sig; BYTE sigRet = 0; DWORD bytes; if (sig >= PG_SIGNAL_COUNT || sig <= 0) { errno = EINVAL; return -1; } if (pid <= 0) { /* No support for process groups */ errno = EINVAL; return -1; } wsprintf(pipename, "\\\\.\\pipe\\pgsignal_%i", pid); if (!CallNamedPipe(pipename, &sigData, 1, &sigRet, 1, &bytes, 1000)) { if (GetLastError() == ERROR_FILE_NOT_FOUND) errno = ESRCH; else if (GetLastError() == ERROR_ACCESS_DENIED) errno = EPERM; else errno = EINVAL; return -1; } if (bytes != 1 || sigRet != sig) { errno = ESRCH; return -1; } return 0; } /* APC callback scheduled on main thread when signals are fired */ static void CALLBACK pg_signal_apc(ULONG_PTR param) { dispatch_queued_signals(); } /* * All functions below execute on the signal handler thread * and must be synchronized as such! * NOTE! The only global variable that can be used is * pg_signal_queue! */ void pg_queue_signal(int signum) { if (signum >= PG_SIGNAL_COUNT || signum < 0) return; EnterCriticalSection(&pg_signal_crit_sec); pg_signal_queue |= sigmask(signum); LeaveCriticalSection(&pg_signal_crit_sec); QueueUserAPC(pg_signal_apc, pgwin32_main_thread_handle, (ULONG_PTR) NULL); } /* Signal dispatching thread */ static DWORD WINAPI pg_signal_dispatch_thread(LPVOID param) { HANDLE pipe = (HANDLE) param; BYTE sigNum; DWORD bytes; if (!ReadFile(pipe, &sigNum, 1, &bytes, NULL)) { /* Client died before sending */ CloseHandle(pipe); return 0; } if (bytes != 1) { /* Received bytes over signal pipe (should be 1) */ CloseHandle(pipe); return 0; } WriteFile(pipe, &sigNum, 1, &bytes, NULL); /* Don't care if it works * or not.. */ FlushFileBuffers(pipe); DisconnectNamedPipe(pipe); CloseHandle(pipe); pg_queue_signal(sigNum); return 0; } /* Signal handling thread */ static DWORD WINAPI pg_signal_thread(LPVOID param) { char pipename[128]; HANDLE pipe = INVALID_HANDLE_VALUE; wsprintf(pipename, "\\\\.\\pipe\\pgsignal_%i", GetCurrentProcessId()); for (;;) { BOOL fConnected; HANDLE hThread; pipe = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 16, 16, 1000, NULL); if (pipe == INVALID_HANDLE_VALUE) { fprintf(stderr, gettext("Failed to create signal listener pipe: %i. Retrying.\n"), (int) GetLastError()); SleepEx(500, TRUE); continue; } fConnected = ConnectNamedPipe(pipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); if (fConnected) { hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) pg_signal_dispatch_thread, (LPVOID) pipe, 0, NULL); if (hThread == INVALID_HANDLE_VALUE) fprintf(stderr, gettext("Failed to create signal dispatch thread: %i\n"), (int) GetLastError()); else CloseHandle(hThread); } else /* Connection failed. Cleanup and try again */ CloseHandle(pipe); } return 0; } /* Console control handler will execute on a thread created by the OS at the time of invocation */ static BOOL WINAPI pg_console_handler(DWORD dwCtrlType) { if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT || dwCtrlType == CTRL_CLOSE_EVENT || dwCtrlType == CTRL_SHUTDOWN_EVENT) { pg_queue_signal(SIGINT); return TRUE; } return FALSE; } #endif