libreoffice-online/kit/ForKit.cpp
Michael Meeks 884a841cde Logging: wrap poco and simplify logging.
Centralize more logging functionality in Log.cpp, simplify and
wrap underlying logging APIs better.

Code is much more generic, and hides implementation details
much more thoroughly, while keeping the same API / wrappers.

To do this we have to sub-class Poco::Logger to get access to
its generic 'log' method instead of a mess of in-line wrappers,
this lets us avoid lots of code.

Change-Id: I541d3aef49f99ce582655c5102a0041bf84cd56a
Signed-off-by: Michael Meeks <michael.meeks@collabora.com>
2024-04-24 10:59:51 +01:00

842 lines
27 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* Copyright the Collabora Online contributors.
*
* SPDX-License-Identifier: MPL-2.0
*
* 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/.
*/
/*
* A very simple, single threaded helper to efficiently pre-init and
* spawn lots of kits as children.
*/
#include <config.h>
#include <config_version.h>
#ifndef __FreeBSD__
#include <sys/capability.h>
#endif
#include <sys/types.h>
#include <sys/wait.h>
#include <sysexits.h>
#include <atomic>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <thread>
#include <chrono>
#include <Poco/Path.h>
#include <Poco/URI.h>
#include <Common.hpp>
#include "Kit.hpp"
#include "SetupKitEnvironment.hpp"
#include <Log.hpp>
#include <Simd.hpp>
#include <Unit.hpp>
#include <Util.hpp>
#include <WebSocketHandler.hpp>
#include <common/FileUtil.hpp>
#include <common/JailUtil.hpp>
#include <common/Seccomp.hpp>
#include <common/SigUtil.hpp>
#include <common/security.h>
#include <common/ConfigUtil.hpp>
#include <common/Watchdog.hpp>
#include <kit/DeltaSimd.h>
static bool NoCapsForKit = false;
static bool NoSeccomp = false;
#if ENABLE_DEBUG
static bool SingleKit = false;
#endif
static std::string UserInterface;
static bool DisplayVersion = false;
static std::string UnitTestLibrary;
static std::string LogLevel;
static std::string LogLevelStartup;
static std::atomic<unsigned> ForkCounter(0);
/// The [child pid -> jail path] map.
static std::map<pid_t, std::string> childJails;
/// The jails that need cleaning up. This should be small.
static std::vector<std::string> cleanupJailPaths;
/// The Main polling main-loop of this (single threaded) process
static std::unique_ptr<SocketPoll> ForKitPoll;
extern "C" { void dump_forkit_state(void); /* easy for gdb */ }
void dump_forkit_state()
{
std::ostringstream oss;
oss << "Forkit: " << ForkCounter << " forks\n"
<< " LogLevel: " << LogLevel << "\n"
<< " LogLevelStartup: " << LogLevelStartup << "\n"
<< " unit test: " << UnitTestLibrary << "\n"
<< " NoCapsForKit: " << NoCapsForKit << "\n"
<< " NoSeccomp: " << NoSeccomp << "\n"
#if ENABLE_DEBUG
<< " SingleKit: " << SingleKit << "\n"
#endif
<< " ClientPortNumber: " << ClientPortNumber << "\n"
<< " MasterLocation: " << MasterLocation
<< "\n";
const std::string msg = oss.str();
fprintf(stderr, "%s", msg.c_str());
LOG_TRC(msg);
}
class ServerWSHandler;
// We have a single thread and a single connection so we won't bother with
// access synchronization
std::shared_ptr<ServerWSHandler> WSHandler;
class ServerWSHandler final : public WebSocketHandler
{
std::string _socketName;
public:
ServerWSHandler(const std::string& socketName) :
WebSocketHandler(/* isClient = */ true, /* isMasking */ false),
_socketName(socketName)
{
}
protected:
void handleMessage(const std::vector<char>& data) override
{
std::string message(data.data(), data.size());
if (!Util::isMobileApp() && UnitKit::get().filterKitMessage(this, message))
return;
StringVector tokens = StringVector::tokenize(message);
LOG_DBG(_socketName << ": recv [" <<
[&](auto& log)
{
for (const auto& token : tokens)
{
log << tokens.getParam(token) << ' ';
}
});
// Note: Syntax or parsing errors here are unexpected and fatal.
if (SigUtil::getTerminationFlag())
{
LOG_DBG("Termination flag set: skip message processing");
}
else if (tokens.size() == 2 && tokens.equals(0, "spawn"))
{
const int count = std::stoi(tokens[1]);
if (count > 0)
{
LOG_INF("Setting to spawn " << tokens[1] << " child" << (count == 1 ? "" : "ren") << " per request.");
ForkCounter = count;
}
else
{
LOG_WRN("Cannot spawn " << tokens[1] << " children as requested.");
}
}
else if (tokens.size() == 2 && tokens.equals(0, "setloglevel"))
{
// Set environment variable so that new children will also set their log levels accordingly.
setenv("COOL_LOGLEVEL", tokens[1].c_str(), 1);
Log::setLevel(tokens[1]);
}
else if (tokens.size() == 3 && tokens.equals(0, "setconfig"))
{
// Currently only rlimit entries are supported.
if (!Rlimit::handleSetrlimitCommand(tokens))
{
LOG_ERR("Unknown setconfig command: " << message);
}
}
else if (tokens.size() == 2 && tokens.equals(0, "addfont"))
{
// Tell core to use that font file
std::string fontFile = tokens[1];
assert(loKitPtr);
loKitPtr->pClass->setOption(loKitPtr, "addfont", Poco::URI(Poco::Path(fontFile)).toString().c_str());
}
else if (tokens.equals(0, "exit"))
{
LOG_INF("Setting TerminationFlag due to 'exit' command from parent.");
SigUtil::setTerminationFlag();
}
else
{
LOG_ERR("Bad or unknown token [" << tokens[0] << ']');
}
}
void onDisconnect() override
{
if (Util::isMobileApp())
return;
LOG_ERR("ForKit connection lost without exit arriving from wsd. Setting TerminationFlag");
SigUtil::setTerminationFlag();
}
};
#ifndef __FreeBSD__
static bool haveCapability(cap_value_t capability)
{
cap_t caps = cap_get_proc();
if (caps == nullptr)
{
LOG_SFL("cap_get_proc() failed");
return false;
}
char *cap_name = cap_to_name(capability);
cap_flag_value_t value;
if (cap_get_flag(caps, capability, CAP_EFFECTIVE, &value) == -1)
{
if (cap_name)
{
LOG_SFL("cap_get_flag failed for " << cap_name);
cap_free(cap_name);
}
else
{
LOG_SFL("cap_get_flag failed for capability " << capability);
}
return false;
}
if (value != CAP_SET)
{
if (cap_name)
{
LOG_ERR("Capability " << cap_name << " is not set for the coolforkit program.");
cap_free(cap_name);
}
else
{
LOG_ERR("Capability " << capability << " is not set for the coolforkit program.");
}
return false;
}
if (cap_name)
{
LOG_INF("Have capability " << cap_name);
cap_free(cap_name);
}
else
{
LOG_INF("Have capability " << capability);
}
return true;
}
static bool haveCorrectCapabilities()
{
bool result = true;
// Do check them all, don't shortcut with &&
if (!haveCapability(CAP_SYS_CHROOT))
result = false;
if (!haveCapability(CAP_FOWNER))
result = false;
if (!haveCapability(CAP_CHOWN))
result = false;
return result;
}
#else
static bool haveCorrectCapabilities()
{
// chroot() can only be called by root
return getuid() == 0;
}
#endif // __FreeBSD__
/// Check if some previously forked kids have died.
static void cleanupChildren()
{
if (Util::isKitInProcess())
return;
pid_t exitedChildPid;
int status = 0;
int segFaultCount = 0;
LOG_TRC("cleanupChildren with " << childJails.size()
<< (childJails.size() == 1 ? " child" : " children"));
// Reap quickly without doing slow cleanup so WSD can spawn more rapidly.
while ((exitedChildPid = waitpid(-1, &status, WUNTRACED | WNOHANG)) > 0)
{
const auto it = childJails.find(exitedChildPid);
if (it != childJails.end())
{
if (WIFSIGNALED(status) && (WTERMSIG(status) == SIGSEGV ||
WTERMSIG(status) == SIGBUS ||
WTERMSIG(status) == SIGABRT))
{
++segFaultCount;
std::string noteCrashFile(it->second + "/tmp/kit-crashed");
int noteCrashFD = open(noteCrashFile.c_str(), O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
if (noteCrashFD < 0)
LOG_ERR("Couldn't create file: " << noteCrashFile << " due to error: " << strerror(errno));
else
close(noteCrashFD);
}
LOG_INF("Child " << exitedChildPid << " has exited, will remove its jail [" << it->second << "].");
cleanupJailPaths.emplace_back(it->second);
childJails.erase(it);
if (childJails.empty() && !SigUtil::getTerminationFlag())
{
// We ran out of kits and we aren't terminating.
LOG_WRN("No live Kits exist, and we are not terminating yet.");
}
}
else
{
LOG_ERR("Unknown child " << exitedChildPid << " has exited");
}
}
if (Log::traceEnabled())
{
std::ostringstream oss;
for (const auto& pair : childJails)
oss << pair.first << ' ';
LOG_TRC("cleanupChildren reaped " << cleanupJailPaths.size() << " children to have "
<< childJails.size() << " left: " << oss.str());
}
if (segFaultCount)
{
if (WSHandler)
{
std::stringstream stream;
stream << "segfaultcount " << segFaultCount << '\n';
int ret = WSHandler->sendMessage(stream.str());
if (ret == -1)
{
LOG_WRN("Could not send 'segfaultcount' message through websocket");
}
else
{
LOG_WRN("Successfully sent 'segfaultcount' message " << stream.str());
}
}
}
// Now delete the jails.
auto i = cleanupJailPaths.size();
while (i-- > 0)
{
const std::string path = cleanupJailPaths[i];
// don't delete jails where there was a crash until it ~3 minutes old
std::string noteCrashFile(path + "/tmp/kit-crashed");
auto noteStat = FileUtil::Stat(noteCrashFile);
if (noteStat.good())
{
time_t modifiedTimeSec = noteStat.modifiedTimeMs() / 1000;
if (time(nullptr) < modifiedTimeSec + 180)
continue;
}
JailUtil::tryRemoveJail(path);
const FileUtil::Stat st(path);
if (st.good() && st.isDirectory())
LOG_DBG("Could not remove jail path [" << path << "]. Will retry later.");
else
cleanupJailPaths.erase(cleanupJailPaths.begin() + i);
}
}
void sleepForDebugger()
{
Util::sleepFromEnvIfSet("Kit", "SLEEPKITFORDEBUGGER");
}
static int createLibreOfficeKit(const std::string& childRoot,
const std::string& sysTemplate,
const std::string& loTemplate,
bool queryVersion = false)
{
// Generate a jail ID to be used for in the jail path.
const std::string jailId = Util::rng::getFilename(16);
// Update the dynamic files as necessary.
JailUtil::SysTemplate::updateDynamicFiles(sysTemplate);
// Used to label the spare kit instances
static size_t spareKitId = 0;
++spareKitId;
LOG_DBG("Forking a coolkit process with jailId: " << jailId << " as spare coolkit #"
<< spareKitId << '.');
const auto startForkingTime = std::chrono::steady_clock::now();
pid_t pid = 0;
if (Util::isKitInProcess())
{
std::thread([childRoot, jailId, sysTemplate, loTemplate, queryVersion] {
sleepForDebugger();
lokit_main(childRoot, jailId, sysTemplate, loTemplate, true, true, queryVersion,
DisplayVersion, spareKitId);
})
.detach();
}
else
{
/* We are about to fork, but not exec. After a fork the child has
only one thread, but a copy of the watchdog object.
Stop the watchdog thread before fork, let the child discard
its copy of the watchdog that is now in a discardable state,
and allow it to create a new one on next SocketPoll ctor */
const bool hasWatchDog(SocketPoll::PollWatchdog);
if (hasWatchDog)
SocketPoll::PollWatchdog->joinThread();
pid = fork();
if (!pid)
{
// Child
// sort out thread local variables to get logging right from
// as early as possible.
Util::setThreadName("kit_spare_" + Util::encodeId(spareKitId, 3));
// Close the pipe from coolwsd
close(0);
// Close the ForKit main-loop's sockets
if (ForKitPoll)
ForKitPoll->closeAllSockets();
// else very first kit process spawned
SigUtil::setSigChildHandler(nullptr);
// Throw away inherited watchdog, which will let a new one for this
// child be created on demand
SocketPoll::PollWatchdog.reset();
UnitKit::get().postFork();
sleepForDebugger();
lokit_main(childRoot, jailId, sysTemplate, loTemplate, NoCapsForKit, NoSeccomp,
queryVersion, DisplayVersion, spareKitId);
}
else
{
if (hasWatchDog)
{
// restart parent watchdog if there was one
SocketPoll::PollWatchdog->startThread();
}
// Parent
if (pid < 0)
{
LOG_SYS("Fork failed");
}
else
{
LOG_INF("Forked kit [" << pid << ']');
childJails[pid] = childRoot + jailId;
}
UnitKit::get().launchedKit(pid);
}
}
const auto duration = (std::chrono::steady_clock::now() - startForkingTime);
const auto durationMs = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
LOG_TRC("Forking child took " << durationMs);
return pid;
}
void forkLibreOfficeKit(const std::string& childRoot,
const std::string& sysTemplate,
const std::string& loTemplate)
{
// Cleanup first, to reduce disk load.
cleanupChildren();
if (ForkCounter > 0)
{
// Create as many as requested.
const size_t count = ForkCounter;
LOG_INF("Spawning " << count << " new child" << (count == 1 ? "." : "ren."));
const size_t retry = count * 2;
for (size_t i = 0; ForkCounter > 0 && i < retry; ++i)
{
if (ForkCounter-- <= 0 || createLibreOfficeKit(childRoot, sysTemplate, loTemplate) < 0)
{
LOG_ERR("Failed to create a kit process.");
++ForkCounter;
}
}
}
}
static void printArgumentHelp()
{
std::cout << "Usage: coolforkit [OPTION]..." << std::endl;
std::cout << " Single-threaded process that spawns lok instances" << std::endl;
std::cout << " Note: Running this standalone is not possible. It is spawned by coolwsd" << std::endl;
std::cout << " and is controlled via a pipe." << std::endl;
std::cout << "" << std::endl;
}
extern "C" {
static void wakeupPoll(uint32_t /*pid*/)
{
if (ForKitPoll)
ForKitPoll->wakeup();
}
}
int forkit_main(int argc, char** argv)
{
/*WARNING: PRIVILEGED CODE CHECKING START */
/*WARNING*/ // early check for avoiding the security check for username 'cool'
/*WARNING*/ // (deliberately only this, not moving the entire parameter parsing here)
/*WARNING*/ bool checkCoolUser = true;
/*WARNING*/ std::string disableCoolUserChecking("--disable-cool-user-checking");
/*WARNING*/ for (int i = 1; checkCoolUser && (i < argc); ++i)
/*WARNING*/ {
/*WARNING*/ if (disableCoolUserChecking == argv[i])
/*WARNING*/ checkCoolUser = false;
/*WARNING*/ }
/*WARNING*/ if (!hasCorrectUID("coolforkit"))
/*WARNING*/ {
/*WARNING*/ // don't allow if any capability is set (unless root; who runs this
/*WARNING*/ // as root or runs this in a container and provides --disable-cool-user-checking knows what they
/*WARNING*/ // are doing)
/*WARNING*/ if (hasUID("root"))
/*WARNING*/ {
/*WARNING*/ // This is fine, the 'root' can do anything anyway
/*WARNING*/ }
/*WARNING*/ else if (isInContainer())
/*WARNING*/ {
/*WARNING*/ // This is fine, we are confined in the container anyway
/*WARNING*/ }
/*WARNING*/ else if (hasAnyCapability())
/*WARNING*/ {
/*WARNING*/ if (!checkCoolUser)
/*WARNING*/ LOG_FTL("Security: --disable-cool-user-checking failed, coolforkit has some capabilities set.");
/*WARNING*/ LOG_FTL("Aborting.");
/*WARNING*/ return EX_SOFTWARE;
/*WARNING*/ }
/*WARNING*/ // even without the capabilities, don't run unless the user really knows
/*WARNING*/ // what they are doing, and provided a --disable-cool-user-checking
/*WARNING*/ if (checkCoolUser)
/*WARNING*/ {
/*WARNING*/ LOG_FTL("Aborting.");
/*WARNING*/ return EX_SOFTWARE;
/*WARNING*/ }
/*WARNING*/ LOG_ERR("Security: Check for the 'cool' username overridden on the command line.");
/*WARNING*/ }
/*WARNING: PRIVILEGED CODE CHECKING END */
// Continue in privileged mode, but only if:
// * the user is 'cool' (privileged user)
// * the user is 'root', and --disable-cool-user-checking was provided
// Alternatively allow running in non-privileged mode (with --nocaps), if:
// * the user is a non-priviled user, the binary is not privileged
// either (no caps set), and --disable-cool-user-checking was provided
Util::sleepFromEnvIfSet("Forkit", "SLEEPFORDEBUGGER");
if (!Util::isKitInProcess())
{
// Already set by COOLWSD.cpp in kit in process
SigUtil::setFatalSignals("forkit startup of " COOLWSD_VERSION " " COOLWSD_VERSION_HASH);
}
else
{
// No capabilities by default for kit in process
NoCapsForKit = true;
NoSeccomp = true;
#if ENABLE_DEBUG
SingleKit = true;
#endif
}
if (simd::init())
simd_deltaInit();
if (!Util::isKitInProcess())
Util::setApplicationPath(Poco::Path(argv[0]).parent().toString());
// Initialization
const bool logToFile = std::getenv("COOL_LOGFILE");
const char* logFilename = std::getenv("COOL_LOGFILENAME");
const char* logLevel = std::getenv("COOL_LOGLEVEL");
const char* logLevelStartup = std::getenv("COOL_LOGLEVEL_STARTUP");
const char* logColor = std::getenv("COOL_LOGCOLOR");
std::map<std::string, std::string> logProperties;
if (logToFile && logFilename)
{
logProperties["path"] = std::string(logFilename);
}
LogLevelStartup = logLevelStartup ? logLevelStartup : "trace";
Log::initialize("frk", LogLevelStartup, logColor != nullptr, logToFile, logProperties);
LogLevel = logLevel ? logLevel : "trace";
if (LogLevel != LogLevelStartup)
{
LOG_INF("Setting log-level to [" << LogLevelStartup << " and delaying "
"setting to configured [" << LogLevel << "] until after Forkit initialization.");
}
std::string childRoot;
std::string sysTemplate;
std::string loTemplate;
for (int i = 0; i < argc; ++i)
{
char *cmd = argv[i];
char *eq;
if (std::strstr(cmd, "--systemplate=") == cmd)
{
eq = std::strchr(cmd, '=');
sysTemplate = std::string(eq+1);
}
else if (std::strstr(cmd, "--lotemplate=") == cmd)
{
eq = std::strchr(cmd, '=');
loTemplate = std::string(eq+1);
}
else if (std::strstr(cmd, "--childroot=") == cmd)
{
eq = std::strchr(cmd, '=');
childRoot = std::string(eq+1);
}
else if (std::strstr(cmd, "--clientport=") == cmd)
{
eq = std::strchr(cmd, '=');
ClientPortNumber = std::stoll(std::string(eq+1));
}
else if (std::strstr(cmd, "--masterport=") == cmd)
{
eq = std::strchr(cmd, '=');
MasterLocation = std::string(eq+1);
}
else if (std::strstr(cmd, "--version") == cmd)
{
std::string version, hash;
Util::getVersionInfo(version, hash);
std::cout << "coolforkit version details: " << version << " - " << hash << std::endl;
DisplayVersion = true;
}
else if (std::strstr(cmd, "--rlimits") == cmd)
{
eq = std::strchr(cmd, '=');
const std::string rlimits = std::string(eq+1);
StringVector tokens = StringVector::tokenize(rlimits, ';');
for (const auto& cmdLimit : tokens)
{
const std::pair<std::string, std::string> pair = Util::split(tokens.getParam(cmdLimit), ':');
StringVector tokensLimit;
tokensLimit.push_back("setconfig");
tokensLimit.push_back(pair.first);
tokensLimit.push_back(pair.second);
if (!Rlimit::handleSetrlimitCommand(tokensLimit))
{
LOG_ERR("Unknown rlimits command: " << tokens.getParam(cmdLimit));
}
}
}
else if (std::strstr(cmd, "--unattended") == cmd)
{
SigUtil::setUnattended();
}
#if ENABLE_DEBUG
// this process has various privileges - don't run arbitrary code.
else if (std::strstr(cmd, "--unitlib=") == cmd)
{
eq = std::strchr(cmd, '=');
UnitTestLibrary = std::string(eq+1);
}
else if (std::strstr(cmd, "--singlekit") == cmd)
{
SingleKit = true;
}
#endif
// we are running in a lower-privilege mode - with no chroot
else if (std::strstr(cmd, "--nocaps") == cmd)
{
LOG_ERR("Security: Running without the capability to enter a chroot jail is ill advised.");
NoCapsForKit = true;
}
// we are running without seccomp protection
else if (std::strstr(cmd, "--noseccomp") == cmd)
{
LOG_ERR("Security: Running without the ability to filter system calls is ill advised.");
NoSeccomp = true;
}
else if (std::strstr(cmd, "--ui") == cmd)
{
eq = std::strchr(cmd, '=');
UserInterface = std::string(eq+1);
if (UserInterface != "classic" && UserInterface != "notebookbar")
UserInterface = "notebookbar";
}
}
if (sysTemplate.empty() || loTemplate.empty() || childRoot.empty())
{
printArgumentHelp();
return EX_USAGE;
}
if (!Util::isKitInProcess() && !UnitBase::init(UnitBase::UnitType::Kit, UnitTestLibrary))
{
LOG_FTL("Failed to load kit unit test library");
return EX_USAGE;
}
setupKitEnvironment(UserInterface);
if (!std::getenv("LD_BIND_NOW")) // must be set by parent.
LOG_INF("Note: LD_BIND_NOW is not set.");
if (!NoCapsForKit && !haveCorrectCapabilities())
{
LOG_FTL("Capabilities are not set for the coolforkit program.");
LOG_FTL("Please make sure that the current partition was *not* mounted with the 'nosuid' option.");
LOG_FTL("If you are on SLES11, please set 'file_caps=1' as kernel boot option.");
return EX_SOFTWARE;
}
// Initialize LoKit
if (!globalPreinit(loTemplate))
{
LOG_FTL("Failed to preinit lokit.");
Util::forcedExit(EX_SOFTWARE);
}
if (Util::ThreadCounter().count() != 1)
LOG_ERR("forkit has more than a single thread after pre-init");
// Link the network and system files in sysTemplate, if possible.
JailUtil::SysTemplate::setupDynamicFiles(sysTemplate);
// Make dev/[u]random point to the writable devices in tmp/dev/.
JailUtil::SysTemplate::setupRandomDeviceLinks(sysTemplate);
if (!Util::isKitInProcess())
{
// Parse the configuration.
const auto conf = std::getenv("COOL_CONFIG");
config::initialize(std::string(conf ? conf : std::string()));
EnableExperimental = config::getBool("experimental_features", false);
}
Util::setThreadName("forkit");
LOG_INF("Preinit stage OK.");
// We must have at least one child, more are created dynamically.
// Ask this first child to send version information to master process and trace startup.
::setenv("COOL_TRACE_STARTUP", "1", 1);
const pid_t forKitPid = createLibreOfficeKit(childRoot, sysTemplate, loTemplate, true);
if (forKitPid < 0)
{
LOG_FTL("Failed to create a kit process.");
Util::forcedExit(EX_SOFTWARE);
}
// No need to trace subsequent children.
::unsetenv("COOL_TRACE_STARTUP");
if (LogLevel != LogLevelStartup)
{
LOG_INF("Forkit initialization complete: setting log-level to [" << LogLevel << "] as configured.");
Log::setLevel(LogLevel);
}
// The SocketPoll ctor which may, depending on COOL_WATCHDOG env variable,
// want to override the SIG2 handler so set user signal handlers before
// that otherwise that choice is overwritten
SigUtil::setUserSignals();
ForKitPoll.reset(new SocketPoll (Util::getThreadName()));
ForKitPoll->runOnClientThread(); // We will do the polling on this thread.
// Reap zombies when we get the signal
SigUtil::setSigChildHandler(wakeupPoll);
WSHandler = std::make_shared<ServerWSHandler>("forkit_ws");
if (!Util::isMobileApp() &&
!ForKitPoll->insertNewUnixSocket(MasterLocation, FORKIT_URI, WSHandler))
{
LOG_SFL("Failed to connect to WSD. Will exit.");
Util::forcedExit(EX_SOFTWARE);
}
const int parentPid = getppid();
LOG_INF("ForKit process is ready. Parent: " << parentPid);
while (!SigUtil::getShutdownRequestFlag())
{
UnitKit::get().invokeForKitTest();
ForKitPoll->poll(std::chrono::seconds(POLL_FORKIT_TIMEOUT_SECS));
SigUtil::checkDumpGlobalState(dump_forkit_state);
// When our parent exits, we are assigned a new parent (typically init).
if (getppid() != parentPid)
{
LOG_SFL("Parent process has died. Will exit now.");
break;
}
#if ENABLE_DEBUG
if (!SingleKit)
#endif
// new kits are launched primarily after a 'spawn' message
if (!Util::isKitInProcess() && !SigUtil::getTerminationFlag())
forkLibreOfficeKit(childRoot, sysTemplate, loTemplate);
}
const int returnValue = UnitBase::uninit();
LOG_INF("ForKit process finished.");
Util::forcedExit(returnValue);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */