Teaspeak-Server/server/src/SignalHandler.cpp
2020-01-24 02:57:58 +01:00

101 lines
4.5 KiB
C++

#include <breakpad/client/linux/handler/exception_handler.h>
#include "TSServer.h"
#include "SignalHandler.h"
#include "ServerManager.h"
#include "InstanceHandler.h"
#include "ShutdownHelper.h"
#include <csignal>
#include <log/LogUtils.h>
#include <experimental/filesystem>
using namespace std;
namespace fs = std::experimental::filesystem;
google_breakpad::ExceptionHandler* globalExceptionHandler = nullptr;
#define SIG(s, c) \
if(signal(s, c) != nullptr) logError(LOG_GENERAL, "Cant setup signal handler for " #s);
extern bool mainThreadDone;
static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded) {
logCritical(LOG_GENERAL, "The server crashed!");
try {
if(!fs::exists(fs::u8path(ts::config::crash_path)))
fs::create_directories(fs::u8path(ts::config::crash_path));
auto path = fs::u8path(descriptor.path());
path = fs::u8path(ts::config::crash_path + "crash_dump_" + path.filename().string());
fs::rename(fs::u8path(descriptor.path()), path);
logCritical(LOG_GENERAL, "Wrote crash dump to " + path.relative_path().string());
} catch (...) {
logCritical(LOG_GENERAL, "Failed to write/move crash dump!");
}
if(std::current_exception()) {
logCritical(LOG_GENERAL, "Exception reached stack root and cause the server to crash!");
logCritical(LOG_GENERAL, " Type: {}", std::current_exception().__cxa_exception_type()->name());
try {
std::rethrow_exception(std::current_exception());
} catch(std::exception& ex) {
logCritical(LOG_GENERAL, " Message: {}", ex.what());
} catch(...) {}
}
logCritical(LOG_GENERAL, "Please report this crash to the TeaSpeak maintainer WolverinDEV");
logCritical(LOG_GENERAL, "Official issue and bug tracker url: https://github.com/TeaSpeak/TeaSpeak/issues");
logCritical(LOG_GENERAL, "Any reports of crashes are useless if you not provide the above generated crashlog!");
logCritical(LOG_GENERAL, "Stopping server");
ts::server::shutdownInstance(ts::config::messages::applicationCrashed);
while(!mainThreadDone) threads::self::sleep_for(chrono::seconds(1));
return succeeded;
}
std::atomic spawn_failed_count = 0;
bool ts::syssignal::setup() {
logMessage(LOG_GENERAL, "Setting up exception handler");
globalExceptionHandler = new google_breakpad::ExceptionHandler(google_breakpad::MinidumpDescriptor("."), nullptr, dumpCallback, nullptr, true, -1);
SIG(SIGTERM, &ts::syssignal::handleStopSignal);
if(isatty(fileno(stdin))) //We cant listen for this signal if stdin ist a atty
SIG(SIGINT, &ts::syssignal::handleStopSignal);
return true;
}
bool ts::syssignal::setup_threads() {
threads::set_global_error_handler([](auto error) {
if(error == threads::ThreadError::HANDLE_DELETE_UNDETACHED) {
logCritical(LOG_GENERAL, "Missed out thread detachment! This could lead to memory leaks!");
return threads::ThreadErrorAction::IGNORE;
} else if(error == threads::ThreadError::SPAWN_FAILED) {
logCritical(LOG_GENERAL, "Spawning a new thread failed!");
if(spawn_failed_count++ == 0) {
logCritical(LOG_GENERAL, "Stopping process!");
try {
std::thread([]{
ts::server::shutdownInstance("Failed to spawn new threads! Safety shutdown");
}).detach();
} catch(...) {
logCritical(LOG_GENERAL, "Failed to spawn shutdown thread (Of cause...). Stopping application directly!");
logCritical(LOG_GENERAL, "If this happens frequently dont forget to checkout std stderr channel for more information");
raise(SIGKILL);
}
}
return threads::ThreadErrorAction::RAISE;
}
return threads::ThreadErrorAction::RAISE;
});
return true;
}
atomic_int signal_count = 0;
void ts::syssignal::handleStopSignal(int signal) {
logMessageFmt(true, LOG_INSTANCE, "Got stop signal ({}). Stopping instance.", signal == SIGTERM ? "SIGTERM" :
signal == SIGINT ? "SIGINT" :
"UNKNOWN (" + to_string(signal) + ")");
if(signal_count++ >= 3) {
logMessageFmt(true, LOG_INSTANCE, "Got stop signal more that tree times. Force exiting instance.");
raise(SIGKILL);
}
ts::server::shutdownInstance();
}