#include #include "VirtualServer.h" #include "SignalHandler.h" #include "VirtualServerManager.h" #include "InstanceHandler.h" #include "ShutdownHelper.h" #include #include #include #include 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"); terminal::finalize_pipe(); 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(); }