[Libreoffice-commits] online.git: loolwsd/Admin.cpp loolwsd/common loolwsd/DocumentBroker.hpp loolwsd/LOOLForKit.cpp loolwsd/LOOLKit.cpp loolwsd/LOOLSession.cpp loolwsd/LOOLWSD.cpp loolwsd/Makefile.am loolwsd/test loolwsd/Unit.cpp loolwsd/Util.cpp loolwsd/Util.hpp

Ashod Nakashian ashod.nakashian at collabora.co.uk
Mon Nov 14 21:20:43 UTC 2016


 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.nakashian at 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 <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at 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 \


More information about the Libreoffice-commits mailing list