diff options
author | Mathias Stearn <mathias@10gen.com> | 2014-04-01 18:11:43 -0400 |
---|---|---|
committer | Mathias Stearn <mathias@10gen.com> | 2014-04-01 19:44:05 -0400 |
commit | 261233b6c11a8282b1f626d496a1743a0b48821d (patch) | |
tree | 3ad229e0cdc69e16368ef62496031fb3ecb22d2a | |
parent | 019f0ccc9efbdb3a76e8e50259dd2921b939d39a (diff) | |
download | mongo-261233b6c11a8282b1f626d496a1743a0b48821d.tar.gz |
SERVER-13429 unify signal handling between mongos and mongod
(cherry picked from commit ad91eb0f75f39c1bb71b5e0ca4279b883cb9fe8d)
-rw-r--r-- | jstests/noPassthrough/exit_logging.js | 16 | ||||
-rw-r--r-- | src/mongo/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/db.cpp | 273 | ||||
-rw-r--r-- | src/mongo/db/initialize_server_global_state.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/initialize_server_global_state.h | 2 | ||||
-rw-r--r-- | src/mongo/db/instance.cpp | 5 | ||||
-rw-r--r-- | src/mongo/s/server.cpp | 119 | ||||
-rw-r--r-- | src/mongo/util/signal_handlers.cpp | 252 | ||||
-rw-r--r-- | src/mongo/util/signal_handlers.h | 15 |
9 files changed, 302 insertions, 391 deletions
diff --git a/jstests/noPassthrough/exit_logging.js b/jstests/noPassthrough/exit_logging.js index 89615b34e8c..32e3dd2d3de 100644 --- a/jstests/noPassthrough/exit_logging.js +++ b/jstests/noPassthrough/exit_logging.js @@ -18,8 +18,10 @@ function makeRegExMatchFn(pattern) { return function (text) { - assert(pattern.test(text), - "Log contents did not match " + pattern); + if (!pattern.test(text)) { + print(text); + doassert("Log contents did not match " + pattern); + } } } @@ -42,16 +44,16 @@ return; } - // testShutdownLogging( - // function (conn) { conn.getDB('admin').shutdownServer() }, - // makeRegExMatchFn(/shutdown command received[\s\S]*dbexit: really exiting now/)); + testShutdownLogging( + function (conn) { conn.getDB('admin').shutdownServer() }, + makeRegExMatchFn(/shutdown command received[\s\S]*dbexit: really exiting now/)); testShutdownLogging( makeShutdownByCrashFn('fault'), - makeRegExMatchFn(/Invalid access at address[\s\S]*abruptQuitWithAddrSignal/)); + makeRegExMatchFn(/Invalid access at address[\s\S]*printStackTrace/)); testShutdownLogging( makeShutdownByCrashFn('abort'), - makeRegExMatchFn(/Got signal[\s\S]*abruptQuit[^W]/)); + makeRegExMatchFn(/Got signal[\s\S]*printStackTrace/)); }()); diff --git a/src/mongo/SConscript b/src/mongo/SConscript index daaa322c05d..58f8406a89b 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -46,7 +46,6 @@ env.Library('foundation', 'util/file.cpp', 'util/log.cpp', 'util/platform_init.cpp', - 'util/signal_handlers.cpp', 'util/text.cpp', 'util/time_support.cpp', 'util/timer.cpp', @@ -901,6 +900,7 @@ mongodAndMongosFiles = [ "db/initialize_server_global_state.cpp", "db/server_extra_log_context.cpp", "db/dbwebserver.cpp", + "util/signal_handlers.cpp", ] env.Library("mongodandmongos", mongodAndMongosFiles, LIBDEPS=["message_server_port"]) diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index 4f19610d1b6..2a5562d08bf 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -88,7 +88,7 @@ #include "mongo/util/options_parser/startup_options.h" #include "mongo/util/ramlog.h" #include "mongo/util/scopeguard.h" -#include "mongo/util/signal_win32.h" +#include "mongo/util/signal_handlers.h" #include "mongo/util/stacktrace.h" #include "mongo/util/startup_test.h" #include "mongo/util/text.h" @@ -108,8 +108,6 @@ namespace mongo { extern int diagLogging; extern int lockFile; - static void setupSignalHandlers(); - static void startSignalProcessingThread(); void exitCleanly( ExitCode code ); #ifdef _WIN32 @@ -934,9 +932,35 @@ MONGO_INITIALIZER_GENERAL(setSSLManagerType, } #endif +#if defined(_WIN32) +namespace mongo { + // the hook for mongoAbort + extern void (*reportEventToSystem)(const char *msg); + static void reportEventToSystemImpl(const char *msg) { + static ::HANDLE hEventLog = RegisterEventSource( NULL, TEXT("mongod") ); + if( hEventLog ) { + std::wstring s = toNativeString(msg); + LPCTSTR txt = s.c_str(); + BOOL ok = ReportEvent( + hEventLog, EVENTLOG_ERROR_TYPE, + 0, 0, NULL, + 1, + 0, + &txt, + 0); + wassert(ok); + } + } +} // namespace mongo +#endif // if defined(_WIN32) + static int mongoDbMain(int argc, char* argv[], char **envp) { static StaticObserver staticObserver; +#if defined(_WIN32) + mongo::reportEventToSystem = &mongo::reportEventToSystemImpl; +#endif + getcurns = ourgetns; setupSignalHandlers(); @@ -987,246 +1011,3 @@ static int mongoDbMain(int argc, char* argv[], char **envp) { dbexit(EXIT_CLEAN); return 0; } - -namespace mongo { - - string getDbContext(); - -#undef out - - -#if !defined(_WIN32) - -} // namespace mongo - -#include <signal.h> -#include <string.h> - -namespace mongo { - - void abruptQuit(int signalNum) { - { - logger::LogstreamBuilder logBuilder(logger::globalLogDomain(), - getThreadName(), - logger::LogSeverity::Severe()); - logBuilder.stream() << - "Got signal: " << signalNum << " (" << strsignal(signalNum) << ").\nBacktrace:"; - printStackTrace(logBuilder.stream()); - } - - // Don't go through normal shutdown procedure. It may make things worse. - ::_exit(EXIT_ABRUPT); - } - - void abruptQuitWithAddrSignal( int signalNum, siginfo_t *siginfo, void * ) { - { - logger::LogstreamBuilder logBuilder(logger::globalLogDomain(), - getThreadName(), - logger::LogSeverity::Severe()); - - std::ostream& oss = logBuilder.stream(); - oss << "Invalid"; - if ( signalNum == SIGSEGV || signalNum == SIGBUS ) { - oss << " access"; - } else { - oss << " operation"; - } - oss << " at address: " << siginfo->si_addr; - } - abruptQuit( signalNum ); - } - - sigset_t asyncSignals; - // The signals in asyncSignals will be processed by this thread only, in order to - // ensure the db and log mutexes aren't held. - void signalProcessingThread() { - while (true) { - int actualSignal = 0; - int status = sigwait( &asyncSignals, &actualSignal ); - fassert(16781, status == 0); - switch (actualSignal) { - case SIGUSR1: - // log rotate signal - fassert(16782, rotateLogs()); - logProcessDetailsForLogRotate(); - break; - case SIGQUIT: - log() << "Received SIGQUIT; terminating."; - _exit(EXIT_ABRUPT); - default: - // interrupt/terminate signal - Client::initThread( "signalProcessingThread" ); - log() << "got signal " << actualSignal << " (" << strsignal( actualSignal ) - << "), will terminate after current cmd ends" << endl; - exitCleanly( EXIT_CLEAN ); - break; - } - } - } - - // this will be called in certain c++ error cases, for example if there are two active - // exceptions - void myterminate() { - printStackTrace(severe().stream() - << "terminate() called, printing stack (if implemented for platform):\n"); - ::abort(); - } - - // this gets called when new fails to allocate memory - void my_new_handler() { - printStackTrace(severe().stream() << "out of memory, printing stack and exiting:\n"); - ::_exit(EXIT_ABRUPT); - } - - void setupSignals_ignoreHelper( int signal ) {} - - void setupSignalHandlers() { - setupCoreSignals(); - - struct sigaction addrSignals; - memset( &addrSignals, 0, sizeof( struct sigaction ) ); - addrSignals.sa_sigaction = abruptQuitWithAddrSignal; - sigemptyset( &addrSignals.sa_mask ); - addrSignals.sa_flags = SA_SIGINFO; - - verify( sigaction(SIGSEGV, &addrSignals, 0) == 0 ); - verify( sigaction(SIGBUS, &addrSignals, 0) == 0 ); - verify( sigaction(SIGILL, &addrSignals, 0) == 0 ); - verify( sigaction(SIGFPE, &addrSignals, 0) == 0 ); - - verify( signal(SIGABRT, abruptQuit) != SIG_ERR ); - verify( signal(SIGPIPE, SIG_IGN) != SIG_ERR ); - - setupSIGTRAPforGDB(); - - // asyncSignals is a global variable listing the signals that should be handled by the - // interrupt thread, once it is started via startSignalProcessingThread(). - sigemptyset( &asyncSignals ); - sigaddset( &asyncSignals, SIGHUP ); - sigaddset( &asyncSignals, SIGINT ); - sigaddset( &asyncSignals, SIGTERM ); - sigaddset( &asyncSignals, SIGQUIT ); - sigaddset( &asyncSignals, SIGUSR1 ); - sigaddset( &asyncSignals, SIGXCPU ); - - set_terminate( myterminate ); - set_new_handler( my_new_handler ); - } - - void startSignalProcessingThread() { - verify( pthread_sigmask( SIG_SETMASK, &asyncSignals, 0 ) == 0 ); - boost::thread it( signalProcessingThread ); - } - -#else // WIN32 - void consoleTerminate( const char* controlCodeName ) { - Client::initThread( "consoleTerminate" ); - log() << "got " << controlCodeName << ", will terminate after current cmd ends" << endl; - exitCleanly( EXIT_KILL ); - } - - BOOL WINAPI CtrlHandler( DWORD fdwCtrlType ) { - - switch( fdwCtrlType ) { - - case CTRL_C_EVENT: - log() << "Ctrl-C signal"; - consoleTerminate( "CTRL_C_EVENT" ); - return TRUE ; - - case CTRL_CLOSE_EVENT: - log() << "CTRL_CLOSE_EVENT signal"; - consoleTerminate( "CTRL_CLOSE_EVENT" ); - return TRUE ; - - case CTRL_BREAK_EVENT: - log() << "CTRL_BREAK_EVENT signal"; - consoleTerminate( "CTRL_BREAK_EVENT" ); - return TRUE; - - case CTRL_LOGOFF_EVENT: - // only sent to services, and only in pre-Vista Windows; FALSE means ignore - return FALSE; - - case CTRL_SHUTDOWN_EVENT: - log() << "CTRL_SHUTDOWN_EVENT signal"; - consoleTerminate( "CTRL_SHUTDOWN_EVENT" ); - return TRUE; - - default: - return FALSE; - } - } - - // called by mongoAbort() - extern void (*reportEventToSystem)(const char *msg); - void reportEventToSystemImpl(const char *msg) { - static ::HANDLE hEventLog = RegisterEventSource( NULL, TEXT("mongod") ); - if( hEventLog ) { - std::wstring s = toNativeString(msg); - LPCTSTR txt = s.c_str(); - BOOL ok = ReportEvent( - hEventLog, EVENTLOG_ERROR_TYPE, - 0, 0, NULL, - 1, - 0, - &txt, - 0); - wassert(ok); - } - } - - void myPurecallHandler() { - printStackTrace(); - mongoAbort("pure virtual"); - } - - void setupSignalHandlers() { - reportEventToSystem = reportEventToSystemImpl; - setWindowsUnhandledExceptionFilter(); - massert(10297, - "Couldn't register Windows Ctrl-C handler", - SetConsoleCtrlHandler(static_cast<PHANDLER_ROUTINE>(CtrlHandler), TRUE)); - _set_purecall_handler( myPurecallHandler ); - } - - void eventProcessingThread() { - std::string eventName = getShutdownSignalName(ProcessId::getCurrent().asUInt32()); - - HANDLE event = CreateEventA(NULL, TRUE, FALSE, eventName.c_str()); - if (event == NULL) { - warning() << "eventProcessingThread CreateEvent failed: " - << errnoWithDescription(); - return; - } - - ON_BLOCK_EXIT(CloseHandle, event); - - int returnCode = WaitForSingleObject(event, INFINITE); - if (returnCode != WAIT_OBJECT_0) { - if (returnCode == WAIT_FAILED) { - warning() << "eventProcessingThread WaitForSingleObject failed: " - << errnoWithDescription(); - return; - } - else { - warning() << "eventProcessingThread WaitForSingleObject failed: " - << errnoWithDescription(returnCode); - return; - } - } - - Client::initThread("eventTerminate"); - log() << "shutdown event signaled, will terminate after current cmd ends"; - exitCleanly(EXIT_CLEAN); - } - - void startSignalProcessingThread() { - if (Command::testCommandsEnabled) { - boost::thread it(eventProcessingThread); - } - } - -#endif // if !defined(_WIN32) - -} // namespace mongo diff --git a/src/mongo/db/initialize_server_global_state.cpp b/src/mongo/db/initialize_server_global_state.cpp index f32250d6d4f..df9f77fdb2e 100644 --- a/src/mongo/db/initialize_server_global_state.cpp +++ b/src/mongo/db/initialize_server_global_state.cpp @@ -366,13 +366,4 @@ namespace mongo { return true; } - static void ignoreSignal( int sig ) {} - - void setupCoreSignals() { -#if !defined(_WIN32) - verify( signal(SIGHUP , ignoreSignal ) != SIG_ERR ); - verify( signal(SIGUSR2, ignoreSignal ) != SIG_ERR ); -#endif - } - } // namespace mongo diff --git a/src/mongo/db/initialize_server_global_state.h b/src/mongo/db/initialize_server_global_state.h index 68f25f77cf2..7085274d7af 100644 --- a/src/mongo/db/initialize_server_global_state.h +++ b/src/mongo/db/initialize_server_global_state.h @@ -51,6 +51,4 @@ namespace mongo { */ void signalForkSuccess(); - void setupCoreSignals(); - } // namespace mongo diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp index caa27969c7d..a7451fd95a8 100644 --- a/src/mongo/db/instance.cpp +++ b/src/mongo/db/instance.cpp @@ -331,10 +331,11 @@ namespace mongo { return ok; } + // Mongod on win32 defines a value for this function. In all other executables it is NULL. void (*reportEventToSystem)(const char *msg) = 0; - void mongoAbort(const char *msg) { - if( reportEventToSystem ) + void mongoAbort(const char *msg) { + if( reportEventToSystem ) reportEventToSystem(msg); severe() << msg; ::abort(); diff --git a/src/mongo/s/server.cpp b/src/mongo/s/server.cpp index f8ba539807a..37311eb1dcb 100644 --- a/src/mongo/s/server.cpp +++ b/src/mongo/s/server.cpp @@ -79,7 +79,6 @@ #include "mongo/util/ramlog.h" #include "mongo/util/scopeguard.h" #include "mongo/util/signal_handlers.h" -#include "mongo/util/signal_win32.h" #include "mongo/util/stacktrace.h" #include "mongo/util/stringutils.h" #include "mongo/util/text.h" @@ -180,119 +179,6 @@ namespace mongo { } }; - void sighandler(int sig) { - dbexit(EXIT_CLEAN, (string("received signal ") + BSONObjBuilder::numStr(sig)).c_str()); - } - - void abruptQuit(int x) { - severe() << "Got signal: " << x << " (" << strsignal( x ) << ")."; - printStackTrace(severe().stream() << "Backtrace: \n"); - ::_exit(EXIT_ABRUPT); - } - - // this gets called when new fails to allocate memory - void my_new_handler() { - printStackTrace(severe().stream() << "out of memory, printing stack and exiting:\n"); - ::_exit(EXIT_ABRUPT); - } - -#ifndef _WIN32 - sigset_t asyncSignals; - - void signalProcessingThread() { - while (true) { - int actualSignal = 0; - int status = sigwait( &asyncSignals, &actualSignal ); - fassert(16779, status == 0); - switch (actualSignal) { - case SIGUSR1: - // log rotate signal - fassert(16780, rotateLogs()); - logProcessDetailsForLogRotate(); - break; - default: - // no one else should be here - fassertFailed(16778); - break; - } - } - } - - void startSignalProcessingThread() { - verify( pthread_sigmask( SIG_SETMASK, &asyncSignals, 0 ) == 0 ); - boost::thread it( signalProcessingThread ); - } -#else - - void eventProcessingThread() { - std::string eventName = getShutdownSignalName(ProcessId::getCurrent().asUInt32()); - - HANDLE event = CreateEventA(NULL, TRUE, FALSE, eventName.c_str()); - if (event == NULL) { - warning() << "eventProcessingThread CreateEvent failed: " - << errnoWithDescription(); - return; - } - - ON_BLOCK_EXIT(CloseHandle, event); - - int returnCode = WaitForSingleObject(event, INFINITE); - if (returnCode != WAIT_OBJECT_0) { - if (returnCode == WAIT_FAILED) { - warning() << "eventProcessingThread WaitForSingleObject failed: " - << errnoWithDescription(); - return; - } - else { - warning() << "eventProcessingThread WaitForSingleObject failed: " - << errnoWithDescription(returnCode); - return; - } - } - - Client::initThread("eventTerminate"); - log() << "shutdown event signaled, will terminate after current cmd ends"; - exitCleanly(EXIT_CLEAN); - } - - void startSignalProcessingThread() { - boost::thread it(eventProcessingThread); - } -#endif // not _WIN32 - - void setupSignalHandlers() { - setupSIGTRAPforGDB(); - setupCoreSignals(); - - signal(SIGTERM, sighandler); - signal(SIGINT, sighandler); -#if defined(SIGXCPU) - signal(SIGXCPU, sighandler); -#endif - -#if defined(SIGQUIT) - signal( SIGQUIT , abruptQuit ); -#endif - signal( SIGSEGV , abruptQuit ); - signal( SIGABRT , abruptQuit ); - signal( SIGFPE , abruptQuit ); -#if defined(SIGBUS) - signal( SIGBUS , abruptQuit ); -#endif -#if defined(SIGPIPE) - signal( SIGPIPE , SIG_IGN ); -#endif - -#ifndef _WIN32 - sigemptyset( &asyncSignals ); - sigaddset( &asyncSignals, SIGUSR1 ); -#endif - - startSignalProcessingThread(); - - setWindowsUnhandledExceptionFilter(); - set_new_handler( my_new_handler ); - } void init() { serverID.init(); @@ -323,7 +209,6 @@ namespace mongo { using namespace mongo; static bool runMongosServer( bool doUpgrade ) { - setupSignalHandlers(); setThreadName( "mongosMain" ); printShardingVersionInfo( false ); @@ -432,6 +317,8 @@ static int _main() { if (!initializeServerGlobalState()) return EXIT_FAILURE; + startSignalProcessingThread(); + // we either have a setting where all processes are in localhost or none are for (std::vector<std::string>::const_iterator it = mongosGlobalParams.configdbs.begin(); it != mongosGlobalParams.configdbs.end(); ++it) { @@ -500,6 +387,8 @@ int mongoSMain(int argc, char* argv[], char** envp) { if (argc < 1) return EXIT_FAILURE; + setupSignalHandlers(); + mongosCommand = argv[0]; Status status = mongo::runGlobalInitializers(argc, argv, envp); diff --git a/src/mongo/util/signal_handlers.cpp b/src/mongo/util/signal_handlers.cpp index dd2ad2b1a7f..9f543afa53d 100644 --- a/src/mongo/util/signal_handlers.cpp +++ b/src/mongo/util/signal_handlers.cpp @@ -28,19 +28,39 @@ * then also delete it in the license file. */ -#include "mongo/pch.h" +#include "mongo/platform/basic.h" -#include <cstdarg> -#include <cstdio> -#include <cstdlib> +#include "mongo/util/signal_handlers.h" -#if !defined(_WIN32) // TODO: windows support -#include <unistd.h> -#endif +#include <boost/thread.hpp> -#include "mongo/platform/backtrace.h" +#include "mongo/db/client.h" +#include "mongo/db/log_process_details.h" +#include "mongo/util/assert_util.h" +#include "mongo/util/exit_code.h" #include "mongo/util/log.h" -#include "mongo/util/signal_handlers.h" +#include "mongo/util/scopeguard.h" +#include "mongo/util/stacktrace.h" + +#if defined(_WIN32) +# include "mongo/util/signal_win32.h" +# include "mongo/util/exception_filter_win32.h" +#else +# include <signal.h> +# include <unistd.h> +#endif + +#if defined(_WIN32) +namespace { + const char* strsignal(int signalNum) { + // should only see SIGABRT on windows + switch (signalNum) { + case SIGABRT: return "SIGABRT"; + default: return "UNKNOWN"; + } + } +} +#endif namespace mongo { @@ -50,6 +70,220 @@ namespace mongo { * All code in this module must be signal-friendly. Before adding any system * call or other dependency, please make sure that this still holds. * + * All code in this file follows this pattern: + * Generic code + * #ifdef _WIN32 + * Windows code + * #else + * Posix code + * #endif + * */ + // everything provides this, but only header is instance.h + void exitCleanly(ExitCode exitCode); + +namespace { + + // this will be called in certain c++ error cases, for example if there are two active + // exceptions + void myTerminate() { + printStackTrace(severe().stream() + << "terminate() called, printing stack (if implemented for platform):\n"); + ::_exit(EXIT_ABRUPT); + } + + // this gets called when new fails to allocate memory + void myNewHandler() { + printStackTrace(severe().stream() << "out of memory, printing stack and exiting:\n"); + ::_exit(EXIT_ABRUPT); + } + + void abruptQuit(int signalNum) { + { + LogstreamBuilder logBuilder = severe(); + logBuilder << + "Got signal: " << signalNum << " (" << strsignal(signalNum) << ").\nBacktrace:"; + printStackTrace(logBuilder.stream()); + } + + // Don't go through normal shutdown procedure. It may make things worse. + ::_exit(EXIT_ABRUPT); + } + +#ifdef _WIN32 + + void consoleTerminate( const char* controlCodeName ) { + Client::initThread( "consoleTerminate" ); + log() << "got " << controlCodeName << ", will terminate after current cmd ends" << endl; + exitCleanly( EXIT_KILL ); + } + + BOOL WINAPI CtrlHandler( DWORD fdwCtrlType ) { + + switch( fdwCtrlType ) { + + case CTRL_C_EVENT: + log() << "Ctrl-C signal"; + consoleTerminate( "CTRL_C_EVENT" ); + return TRUE ; + + case CTRL_CLOSE_EVENT: + log() << "CTRL_CLOSE_EVENT signal"; + consoleTerminate( "CTRL_CLOSE_EVENT" ); + return TRUE ; + + case CTRL_BREAK_EVENT: + log() << "CTRL_BREAK_EVENT signal"; + consoleTerminate( "CTRL_BREAK_EVENT" ); + return TRUE; + + case CTRL_LOGOFF_EVENT: + // only sent to services, and only in pre-Vista Windows; FALSE means ignore + return FALSE; + + case CTRL_SHUTDOWN_EVENT: + log() << "CTRL_SHUTDOWN_EVENT signal"; + consoleTerminate( "CTRL_SHUTDOWN_EVENT" ); + return TRUE; + + default: + return FALSE; + } + } + + void eventProcessingThread() { + std::string eventName = getShutdownSignalName(ProcessId::getCurrent().asUInt32()); + + HANDLE event = CreateEventA(NULL, TRUE, FALSE, eventName.c_str()); + if (event == NULL) { + warning() << "eventProcessingThread CreateEvent failed: " + << errnoWithDescription(); + return; + } + + ON_BLOCK_EXIT(CloseHandle, event); + + int returnCode = WaitForSingleObject(event, INFINITE); + if (returnCode != WAIT_OBJECT_0) { + if (returnCode == WAIT_FAILED) { + warning() << "eventProcessingThread WaitForSingleObject failed: " + << errnoWithDescription(); + return; + } + else { + warning() << "eventProcessingThread WaitForSingleObject failed: " + << errnoWithDescription(returnCode); + return; + } + } + + Client::initThread("eventTerminate"); + log() << "shutdown event signaled, will terminate after current cmd ends"; + exitCleanly(EXIT_CLEAN); + } + +#else + + void abruptQuitWithAddrSignal( int signalNum, siginfo_t *siginfo, void * ) { + { + LogstreamBuilder logBuilder = severe(); + + logBuilder << "Invalid"; + if ( signalNum == SIGSEGV || signalNum == SIGBUS ) { + logBuilder << " access"; + } else { + logBuilder << " operation"; + } + logBuilder << " at address: " << siginfo->si_addr; + } + abruptQuit( signalNum ); + } + + // The signals in asyncSignals will be processed by this thread only, in order to + // ensure the db and log mutexes aren't held. + sigset_t asyncSignals; + void signalProcessingThread() { + Client::initThread( "signalProcessingThread" ); + while (true) { + int actualSignal = 0; + int status = sigwait( &asyncSignals, &actualSignal ); + fassert(16781, status == 0); + switch (actualSignal) { + case SIGUSR1: + // log rotate signal + fassert(16782, rotateLogs()); + logProcessDetailsForLogRotate(); + break; + case SIGQUIT: + log() << "Received SIGQUIT; terminating."; + _exit(EXIT_ABRUPT); + default: + // interrupt/terminate signal + log() << "got signal " << actualSignal << " (" << strsignal( actualSignal ) + << "), will terminate after current cmd ends" << endl; + exitCleanly( EXIT_CLEAN ); + break; + } + } + } +#endif +} // namespace + + void setupSignalHandlers() { + set_terminate( myTerminate ); + set_new_handler( myNewHandler ); + + // SIGABRT is the only signal we want handled by signal handlers on both windows and posix. + invariant( signal(SIGABRT, abruptQuit) != SIG_ERR ); + +#ifdef _WIN32 + _set_purecall_handler( ::abort ); // TODO improve? + setWindowsUnhandledExceptionFilter(); + massert(10297, + "Couldn't register Windows Ctrl-C handler", + SetConsoleCtrlHandler(static_cast<PHANDLER_ROUTINE>(CtrlHandler), TRUE)); + +#else + invariant( signal(SIGHUP , SIG_IGN ) != SIG_ERR ); + invariant( signal(SIGUSR2, SIG_IGN ) != SIG_ERR ); + invariant( signal(SIGPIPE, SIG_IGN) != SIG_ERR ); + + struct sigaction addrSignals; + memset( &addrSignals, 0, sizeof( struct sigaction ) ); + addrSignals.sa_sigaction = abruptQuitWithAddrSignal; + sigemptyset( &addrSignals.sa_mask ); + addrSignals.sa_flags = SA_SIGINFO; + + invariant( sigaction(SIGSEGV, &addrSignals, 0) == 0 ); + invariant( sigaction(SIGBUS, &addrSignals, 0) == 0 ); + invariant( sigaction(SIGILL, &addrSignals, 0) == 0 ); + invariant( sigaction(SIGFPE, &addrSignals, 0) == 0 ); + + + setupSIGTRAPforGDB(); + + // asyncSignals is a global variable listing the signals that should be handled by the + // interrupt thread, once it is started via startSignalProcessingThread(). + sigemptyset( &asyncSignals ); + sigaddset( &asyncSignals, SIGHUP ); + sigaddset( &asyncSignals, SIGINT ); + sigaddset( &asyncSignals, SIGTERM ); + sigaddset( &asyncSignals, SIGQUIT ); + sigaddset( &asyncSignals, SIGUSR1 ); + sigaddset( &asyncSignals, SIGXCPU ); +#endif + } + + void startSignalProcessingThread() { +#ifdef _WIN32 + boost::thread(eventProcessingThread).detach(); +#else + // Mask signals in the current (only) thread. All new threads will inherit this mask. + invariant( pthread_sigmask( SIG_SETMASK, &asyncSignals, 0 ) == 0 ); + // Spawn a thread to capture the signals we just masked off. + boost::thread( signalProcessingThread ).detach(); +#endif + } + } // namespace mongo diff --git a/src/mongo/util/signal_handlers.h b/src/mongo/util/signal_handlers.h index 01f59728868..1813019930f 100644 --- a/src/mongo/util/signal_handlers.h +++ b/src/mongo/util/signal_handlers.h @@ -32,4 +32,19 @@ namespace mongo { + /** + * Sets up handlers for signals and other events like terminate and new_handler. + * + * This must be called very early in main, before runGlobalInitializers(). + */ + void setupSignalHandlers(); + + /** + * Starts the thread to handle asynchronous signals. + * + * This must be the first thread started from the main thread. Call this immediately after + * initializeServerGlobalState(). + */ + void startSignalProcessingThread(); + } // namespace mongo |