summaryrefslogtreecommitdiff
path: root/src/mongo/util/signal_handlers.cpp
diff options
context:
space:
mode:
authorMathias Stearn <mathias@10gen.com>2014-04-01 18:11:43 -0400
committerMathias Stearn <mathias@10gen.com>2014-04-01 19:39:09 -0400
commitad91eb0f75f39c1bb71b5e0ca4279b883cb9fe8d (patch)
tree8360c99e546844d752492e1afc73e9c1e5757350 /src/mongo/util/signal_handlers.cpp
parent11e43325fa2b0139df1eb9c367b984a5b112dbb4 (diff)
downloadmongo-ad91eb0f75f39c1bb71b5e0ca4279b883cb9fe8d.tar.gz
SERVER-13429 unify signal handling between mongos and mongod
Diffstat (limited to 'src/mongo/util/signal_handlers.cpp')
-rw-r--r--src/mongo/util/signal_handlers.cpp252
1 files changed, 243 insertions, 9 deletions
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