4ed820d3d5
and apply the nullptr plugin. Lots of hacking in my LO tree required to make this work, will probably end up needing to add an extra parameter to the LO side. Change-Id: I02ae1dcdece9d9ddf05f7757f6696e3a5d7d1f14 Reviewed-on: https://gerrit.libreoffice.org/32339 Reviewed-by: Tor Lillqvist <tml@collabora.com> Tested-by: Tor Lillqvist <tml@collabora.com>
302 lines
7.9 KiB
C++
302 lines
7.9 KiB
C++
/* -*- 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::mutex SigHandlerTrap;
|
|
|
|
/// Flag to shutdown the server.
|
|
std::atomic<bool> ShutdownFlag;
|
|
|
|
/// Flag to request WSD to notify clients and shutdown.
|
|
static std::atomic<bool> 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<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, nullptr);
|
|
|
|
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 != nullptr)
|
|
{
|
|
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, nullptr);
|
|
sigaction(SIGBUS, &action, nullptr);
|
|
sigaction(SIGABRT, &action, nullptr);
|
|
sigaction(SIGILL, &action, nullptr);
|
|
sigaction(SIGFPE, &action, nullptr);
|
|
|
|
// 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: shuttingdown");
|
|
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: */
|