/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SigUtil.hpp" #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Common.hpp" #include "Log.hpp" #include "Util.hpp" std::atomic TerminationFlag(false); std::mutex SigHandlerTrap; /// Flag to shutdown the server. std::atomic ShutdownFlag; /// Flag to request WSD to notify clients and shutdown. static std::atomic ShutdownRequestFlag(false); namespace SigUtil { const char *signalName(const int signo) { switch (signo) { #define CASE(x) case SIG##x: return "SIG" #x CASE(HUP); CASE(INT); CASE(QUIT); CASE(ILL); CASE(ABRT); CASE(FPE); CASE(KILL); CASE(SEGV); CASE(PIPE); CASE(ALRM); CASE(TERM); CASE(USR1); CASE(USR2); CASE(CHLD); CASE(CONT); CASE(STOP); CASE(TSTP); CASE(TTIN); CASE(TTOU); CASE(BUS); #ifdef SIGPOLL CASE(POLL); #endif CASE(PROF); CASE(SYS); CASE(TRAP); CASE(URG); CASE(VTALRM); CASE(XCPU); CASE(XFSZ); #ifdef SIGEMT CASE(EMT); #endif #ifdef SIGSTKFLT CASE(STKFLT); #endif #if defined(SIGIO) && SIGIO != SIGPOLL CASE(IO); #endif #ifdef SIGPWR CASE(PWR); #endif #ifdef SIGLOST CASE(LOST); #endif CASE(WINCH); #if defined(SIGINFO) && SIGINFO != SIGPWR CASE(INFO); #endif #undef CASE default: return "unknown"; } } static void handleTerminationSignal(const int signal) { if (!ShutdownFlag && signal == SIGINT) { Log::signalLogPrefix(); Log::signalLog(" Shutdown signal received: "); Log::signalLog(signalName(signal)); Log::signalLog("\n"); SigUtil::requestShutdown(); } else { Log::signalLogPrefix(); Log::signalLog(" Forced-Termination signal received: "); Log::signalLog(signalName(signal)); Log::signalLog("\n"); TerminationFlag = true; } } void setTerminationSignals() { struct sigaction action; sigemptyset(&action.sa_mask); action.sa_flags = 0; action.sa_handler = handleTerminationSignal; sigaction(SIGINT, &action, nullptr); sigaction(SIGTERM, &action, nullptr); sigaction(SIGQUIT, &action, nullptr); sigaction(SIGHUP, &action, nullptr); } static char FatalGdbString[256] = { '\0' }; static void handleFatalSignal(const int signal) { std::unique_lock lock(SigHandlerTrap); Log::signalLogPrefix(); Log::signalLog(" Fatal signal received: "); Log::signalLog(signalName(signal)); Log::signalLog("\n"); if (std::getenv("LOOL_DEBUG")) { Log::signalLog(FatalGdbString); LOG_ERR("Sleeping 30s to allow debugging."); sleep(30); } struct sigaction action; sigemptyset(&action.sa_mask); action.sa_flags = 0; action.sa_handler = SIG_DFL; sigaction(signal, &action, NULL); char header[32]; sprintf(header, "Backtrace %d:\n", getpid()); const int maxSlots = 50; void *backtraceBuffer[maxSlots]; int numSlots = backtrace(backtraceBuffer, maxSlots); if (numSlots > 0) { char **symbols = backtrace_symbols(backtraceBuffer, numSlots); if (symbols != NULL) { struct iovec ioVector[maxSlots*2+1]; ioVector[0].iov_base = (void*)header; ioVector[0].iov_len = std::strlen((const char*)ioVector[0].iov_base); for (int i = 0; i < numSlots; i++) { ioVector[1+i*2+0].iov_base = symbols[i]; ioVector[1+i*2+0].iov_len = std::strlen((const char *)ioVector[1+i*2+0].iov_base); ioVector[1+i*2+1].iov_base = (void*)"\n"; ioVector[1+i*2+1].iov_len = 1; } if (writev(STDERR_FILENO, ioVector, numSlots*2+1) == -1) { LOG_SYS("Failed to dump backtrace to stderr."); } } } if (std::getenv("LOOL_DEBUG")) { LOG_ERR("Sleeping 30s to allow debugging."); sleep(30); } // let default handler process the signal kill(Poco::Process::id(), signal); } void setFatalSignals() { struct sigaction action; sigemptyset(&action.sa_mask); action.sa_flags = 0; action.sa_handler = handleFatalSignal; sigaction(SIGSEGV, &action, NULL); sigaction(SIGBUS, &action, NULL); sigaction(SIGABRT, &action, NULL); sigaction(SIGILL, &action, NULL); sigaction(SIGFPE, &action, NULL); // prepare this in advance just in case. std::ostringstream stream; stream << "\nFatal signal! Attach debugger with:\n" << "sudo gdb --pid=" << Poco::Process::id() << "\n or \n" << "sudo gdb --q --n --ex 'thread apply all backtrace full' --batch --pid=" << Poco::Process::id() << "\n"; std::string streamStr = stream.str(); assert (sizeof (FatalGdbString) > strlen(streamStr.c_str()) + 1); strncpy(FatalGdbString, streamStr.c_str(), sizeof(FatalGdbString)); } void requestShutdown() { ShutdownRequestFlag = true; } bool handleShutdownRequest() { if (ShutdownRequestFlag) { LOG_INF("Shutdown requested. Initiating WSD shutdown."); Util::alertAllUsers("close: shutdown"); ShutdownFlag = true; return true; } return false; } bool isShuttingDown() { return ShutdownFlag; } bool killChild(const int pid) { LOG_DBG("Killing PID: " << pid); if (kill(pid, SIGTERM) == 0 || errno == ESRCH) { // Killed or doesn't exist. return true; } LOG_SYS("Error when trying to kill PID: " << pid << ". Will wait for termination."); const auto sleepMs = 50; const auto count = std::max(CHILD_REBALANCE_INTERVAL_MS / sleepMs, 2); for (int i = 0; i < count; ++i) { if (kill(pid, 0) == 0 || errno == ESRCH) { // Doesn't exist. return true; } std::this_thread::sleep_for(std::chrono::milliseconds(sleepMs)); } LOG_WRN("Cannot terminate PID: " << pid); return false; } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */