loolwsd/Admin.cpp | 4 loolwsd/DocumentBroker.hpp | 4 loolwsd/LOOLForKit.cpp | 12 + loolwsd/LOOLKit.cpp | 8 - loolwsd/LOOLSession.cpp | 4 loolwsd/LOOLWSD.cpp | 14 +- loolwsd/Makefile.am | 2 loolwsd/Unit.cpp | 2 loolwsd/Util.cpp | 232 ------------------------------------ loolwsd/Util.hpp | 26 ---- loolwsd/common/SigUtil.cpp | 288 +++++++++++++++++++++++++++++++++++++++++++++ loolwsd/common/SigUtil.hpp | 51 +++++++ loolwsd/test/Makefile.am | 1 13 files changed, 372 insertions(+), 276 deletions(-)
New commits: commit 8072576424351b5e164a049357f6f081d1ffa3ca Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> Date: Mon Nov 14 08:58:04 2016 -0500 loolwsd: move signal and process code to SigUtil files Change-Id: I91c001ef54858d942f8e3fe56d8a6b02cb2bf37e Reviewed-on: https://gerrit.libreoffice.org/30846 Reviewed-by: Ashod Nakashian <ashnak...@gmail.com> Tested-by: Ashod Nakashian <ashnak...@gmail.com> diff --git a/loolwsd/Admin.cpp b/loolwsd/Admin.cpp index dc2aa9d..445fa1e 100644 --- a/loolwsd/Admin.cpp +++ b/loolwsd/Admin.cpp @@ -40,6 +40,8 @@ #include "Unit.hpp" #include "Util.hpp" +#include "common/SigUtil.hpp" + using namespace LOOLProtocol; using Poco::StringTokenizer; @@ -124,7 +126,7 @@ bool AdminRequestHandler::adminCommandHandler(const std::vector<char>& payload) { const auto pid = std::stoi(tokens[1]); LOG_INF("Admin request to kill PID: " << pid); - Util::killChild(pid); + SigUtil::killChild(pid); } catch (std::invalid_argument& exc) { diff --git a/loolwsd/DocumentBroker.hpp b/loolwsd/DocumentBroker.hpp index 2184925..7e94fa2 100644 --- a/loolwsd/DocumentBroker.hpp +++ b/loolwsd/DocumentBroker.hpp @@ -30,6 +30,8 @@ #include "TileCache.hpp" #include "Util.hpp" +#include "common/SigUtil.hpp" + // Forwards. class DocumentBroker; @@ -99,7 +101,7 @@ public: if (_pid != -1 && rude && kill(_pid, 0) != 0 && errno != ESRCH) { LOG_INF("Killing child [" << _pid << "]."); - if (Util::killChild(_pid)) + if (SigUtil::killChild(_pid)) { LOG_ERR("Cannot terminate lokit [" << _pid << "]. Abandoning."); } diff --git a/loolwsd/LOOLForKit.cpp b/loolwsd/LOOLForKit.cpp index 75156a0..6763225 100644 --- a/loolwsd/LOOLForKit.cpp +++ b/loolwsd/LOOLForKit.cpp @@ -32,12 +32,14 @@ #include <Poco/Util/Application.h> #include "Common.hpp" -#include "common/FileUtil.hpp" #include "IoUtil.hpp" #include "LOOLKit.hpp" #include "Log.hpp" #include "Unit.hpp" #include "Util.hpp" + +#include "common/FileUtil.hpp" +#include "common/SigUtil.hpp" #include "security.h" using Poco::Process; @@ -259,7 +261,9 @@ static void printArgumentHelp() int main(int argc, char** argv) { if (!hasCorrectUID("loolforkit")) + { return Application::EXIT_SOFTWARE; + } if (std::getenv("SLEEPFORDEBUGGER")) { @@ -273,6 +277,9 @@ int main(int argc, char** argv) } } + SigUtil::setFatalSignals(); + SigUtil::setTerminationSignals(); + // Initialization const bool logToFile = std::getenv("LOOL_LOGFILE"); const char* logFilename = std::getenv("LOOL_LOGFILENAME"); @@ -286,9 +293,6 @@ int main(int argc, char** argv) Log::initialize("frk", logLevel ? logLevel : "", logColor != nullptr, logToFile, logProperties); - Util::setTerminationSignals(); - Util::setFatalSignals(); - std::string childRoot; std::string loSubPath; std::string sysTemplate; diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp index 435b8a7..48902cb 100644 --- a/loolwsd/LOOLKit.cpp +++ b/loolwsd/LOOLKit.cpp @@ -54,7 +54,7 @@ #include "LOKitHelper.hpp" #include "LOOLKit.hpp" #include "LOOLProtocol.hpp" -#include <LOOLWebSocket.hpp> +#include "LOOLWebSocket.hpp" #include "LibreOfficeKit.hpp" #include "Log.hpp" #include "Png.hpp" @@ -64,6 +64,8 @@ #include "UserMessages.hpp" #include "Util.hpp" +#include "common/SigUtil.hpp" + #define LIB_SOFFICEAPP "lib" "sofficeapp" ".so" #define LIB_MERGED "lib" "mergedlo" ".so" @@ -1279,8 +1281,8 @@ void lokit_main(const std::string& childRoot, bool queryVersion, bool displayVersion) { - Util::setFatalSignals(); - Util::setTerminationSignals(); + SigUtil::setFatalSignals(); + SigUtil::setTerminationSignals(); Util::setThreadName("loolkit"); diff --git a/loolwsd/LOOLSession.cpp b/loolwsd/LOOLSession.cpp index 97ae19e..c503af7 100644 --- a/loolwsd/LOOLSession.cpp +++ b/loolwsd/LOOLSession.cpp @@ -227,10 +227,6 @@ bool LOOLSession::handleInput(const char *buffer, int length) try { LOG_TRC(getName() << ": Recv: " << summary); - if (TerminationFlag) - { - LOG_WRN("Input while terminating: [" << summary << "]."); - } return _handleInput(buffer, length); } diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp index 1ee548c..e6a5f27 100644 --- a/loolwsd/LOOLWSD.cpp +++ b/loolwsd/LOOLWSD.cpp @@ -114,6 +114,8 @@ #include "UserMessages.hpp" #include "Util.hpp" +#include "common/SigUtil.hpp" + using namespace LOOLProtocol; using Poco::Environment; @@ -1846,8 +1848,8 @@ Process::PID LOOLWSD::createForKit() int LOOLWSD::main(const std::vector<std::string>& /*args*/) { - Util::setFatalSignals(); - Util::setTerminationSignals(); + SigUtil::setFatalSignals(); + SigUtil::setTerminationSignals(); // down-pay all the forkit linking cost once & early. Environment::set("LD_BIND_NOW", "1"); @@ -1863,7 +1865,9 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/) char* locale = setlocale(LC_ALL, nullptr); if (locale == nullptr || std::strcmp(locale, "C") == 0) + { setlocale(LC_ALL, "en_US.utf8"); + } if (access(Cache.c_str(), R_OK | W_OK | X_OK) != 0) { @@ -1989,7 +1993,7 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/) { LOG_ERR("Child process [" << pid << "] " << (WCOREDUMP(status) ? "core-dumped" : "died") << - " with " << Util::signalName(WTERMSIG(status))); + " with " << SigUtil::signalName(WTERMSIG(status))); } // Spawn a new forkit and try to dust it off and resume. @@ -2003,7 +2007,7 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/) else if (WIFSTOPPED(status) == true) { LOG_INF("Child process [" << pid << "] stopped with " << - Util::signalName(WSTOPSIG(status))); + SigUtil::signalName(WSTOPSIG(status))); } else if (WIFCONTINUED(status) == true) { @@ -2082,7 +2086,7 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/) // Terminate child processes LOG_INF("Requesting forkit process " << forKitPid << " to terminate."); - Util::requestTermination(forKitPid); + SigUtil::requestTermination(forKitPid); for (auto& child : NewChildren) { child->close(true); diff --git a/loolwsd/Makefile.am b/loolwsd/Makefile.am index e7e69ff..a87599d 100644 --- a/loolwsd/Makefile.am +++ b/loolwsd/Makefile.am @@ -32,6 +32,7 @@ AM_CTAGSFLAGS = $(AM_ETAGSFLAGS) shared_sources = ChildSession.cpp \ common/FileUtil.cpp \ + common/SigUtil.cpp \ IoUtil.cpp \ Log.cpp \ LOOLProtocol.cpp \ @@ -95,6 +96,7 @@ noinst_HEADERS = Admin.hpp \ Exceptions.hpp \ FileServer.hpp \ common/FileUtil.hpp \ + common/SigUtil.hpp \ IoUtil.hpp \ LibreOfficeKit.hpp \ Log.hpp \ diff --git a/loolwsd/Unit.cpp b/loolwsd/Unit.cpp index f269fc7..ca83f4e 100644 --- a/loolwsd/Unit.cpp +++ b/loolwsd/Unit.cpp @@ -22,6 +22,8 @@ #include "Log.hpp" #include "Util.hpp" +#include "common/SigUtil.hpp" + UnitBase *UnitBase::Global = nullptr; static Poco::Thread TimeoutThread("unit timeout"); diff --git a/loolwsd/Util.cpp b/loolwsd/Util.cpp index 85ccd1e..b4aa969 100644 --- a/loolwsd/Util.cpp +++ b/loolwsd/Util.cpp @@ -50,10 +50,6 @@ #include "Log.hpp" #include "Util.hpp" -std::atomic<bool> TerminationFlag(false); -std::atomic<bool> ShutdownFlag(false); -std::mutex SigHandlerTrap; - namespace Util { namespace rng @@ -136,206 +132,6 @@ namespace Util namespace Util { - 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) - { - Log::signalLogPrefix(); - Log::signalLog(" Shutdown signal received: "); - Log::signalLog(signalName(signal)); - Log::signalLog("\n"); - ShutdownFlag = true; - return; - } - - if (!TerminationFlag) - { - 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<std::mutex> 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 requestTermination(const Poco::Process::PID& pid) - { - try - { - Poco::Process::requestTermination(pid); - } - catch(const Poco::Exception& exc) - { - Log::warn("Util::requestTermination: Exception: " + exc.message()); - } - } - int getMemoryUsage(const Poco::Process::PID nPid) { try @@ -408,34 +204,6 @@ namespace Util static std::atomic_int counter(0); return std::to_string(Poco::Process::id()) + "/" + std::to_string(counter++); } - - 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: */ diff --git a/loolwsd/Util.hpp b/loolwsd/Util.hpp index 3abc53e..40bcc30 100644 --- a/loolwsd/Util.hpp +++ b/loolwsd/Util.hpp @@ -27,16 +27,6 @@ #define LOK_USE_UNSTABLE_API #include <LibreOfficeKit/LibreOfficeKitEnums.h> -/// Flag to stop pump loops. -extern std::atomic<bool> TerminationFlag; - -/// Flag to shutdown the server. -extern std::atomic<bool> ShutdownFlag; - -/// Mutex to trap signal handler, if any, -/// and prevent _Exit while collecting backtrace. -extern std::mutex SigHandlerTrap; - namespace Util { namespace rng @@ -82,22 +72,6 @@ namespace Util assert(!mtx.try_lock()); } - /// Returns the name of the signal. - const char* signalName(int signo); - - /// Trap signals to cleanup and exit the process gracefully. - void setTerminationSignals(); - - /// Trap all fatal signals to assist debugging. - void setFatalSignals(); - - void requestTermination(const Poco::Process::PID& pid); - - /// Kills a child process and returns true when - /// child pid is removed from the process table - /// after a certain (short) timeout. - bool killChild(const int pid); - int getMemoryUsage(const Poco::Process::PID nPid); std::string replace(const std::string& s, const std::string& a, const std::string& b); diff --git a/loolwsd/common/SigUtil.cpp b/loolwsd/common/SigUtil.cpp new file mode 100644 index 0000000..f3e7f03 --- /dev/null +++ b/loolwsd/common/SigUtil.cpp @@ -0,0 +1,288 @@ +/* -*- 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 <execinfo.h> +#include <csignal> +#include <sys/poll.h> +#include <sys/prctl.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <sys/vfs.h> +#include <unistd.h> + +#include <atomic> +#include <cassert> +#include <chrono> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <fstream> +#include <iomanip> +#include <iostream> +#include <mutex> +#include <random> +#include <sstream> +#include <string> +#include <thread> + +#include <Poco/Base64Encoder.h> +#include <Poco/ConsoleChannel.h> +#include <Poco/Exception.h> +#include <Poco/Format.h> +#include <Poco/Net/WebSocket.h> +#include <Poco/Process.h> +#include <Poco/RandomStream.h> +#include <Poco/TemporaryFile.h> +#include <Poco/Thread.h> +#include <Poco/Timestamp.h> +#include <Poco/Util/Application.h> + +#include "Common.hpp" +#include "Log.hpp" +#include "Util.hpp" + +std::atomic<bool> TerminationFlag(false); +std::atomic<bool> ShutdownFlag(false); +std::mutex SigHandlerTrap; + +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) + { + Log::signalLogPrefix(); + Log::signalLog(" Shutdown signal received: "); + Log::signalLog(signalName(signal)); + Log::signalLog("\n"); + ShutdownFlag = true; + return; + } + + if (!TerminationFlag) + { + 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<std::mutex> 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 requestTermination(const Poco::Process::PID& pid) + { + try + { + Poco::Process::requestTermination(pid); + } + catch(const Poco::Exception& exc) + { + Log::warn("Util::requestTermination: Exception: " + exc.message()); + } + } + + 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: */ diff --git a/loolwsd/common/SigUtil.hpp b/loolwsd/common/SigUtil.hpp new file mode 100644 index 0000000..13082cf --- /dev/null +++ b/loolwsd/common/SigUtil.hpp @@ -0,0 +1,51 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_SIGNALUTIL_HPP +#define INCLUDED_SIGNALUTIL_HPP + +#include <atomic> +#include <mutex> +#include <string> + +#include <Poco/Process.h> + +/// Flag to stop pump loops. +extern std::atomic<bool> TerminationFlag; + +/// Flag to shutdown the server. +extern std::atomic<bool> ShutdownFlag; + +/// Mutex to trap signal handler, if any, +/// and prevent _Exit while collecting backtrace. +extern std::mutex SigHandlerTrap; + +namespace SigUtil +{ + /// Returns the name of the signal. + const char* signalName(int signo); + + /// Trap signals to cleanup and exit the process gracefully. + void setTerminationSignals(); + + /// Trap all fatal signals to assist debugging. + void setFatalSignals(); + + void requestTermination(const Poco::Process::PID& pid); + + /// Kills a child process and returns true when + /// child pid is removed from the process table + /// after a certain (short) timeout. + bool killChild(const int pid); + +} // end namespace SigUtil + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/loolwsd/test/Makefile.am b/loolwsd/test/Makefile.am index 9de6443..7600797 100644 --- a/loolwsd/test/Makefile.am +++ b/loolwsd/test/Makefile.am @@ -28,6 +28,7 @@ AM_CPPFLAGS = -pthread -I$(top_srcdir) -DBUILDING_TESTS wsd_sources = \ ../common/FileUtil.cpp \ + ../common/SigUtil.cpp \ ../IoUtil.cpp \ ../Log.cpp \ ../LOOLKit.cpp \ _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits