2016-04-05 08:32:10 -05:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2017-12-20 07:06:26 -06:00
|
|
|
#include <config.h>
|
2016-04-12 04:00:33 -05:00
|
|
|
|
2020-11-08 09:24:32 -06:00
|
|
|
#ifndef __FreeBSD__
|
2016-04-05 08:32:10 -05:00
|
|
|
#include <sys/capability.h>
|
2020-11-08 09:24:32 -06:00
|
|
|
#endif
|
loolwsd: include cleanup and organization
A source file (.cpp) must include its own header first.
This insures that the header is self-contained and
doesn't depend on arbitrary (and accidental) includes
before it to compile.
Furthermore, system headers should go next, followed by
C then C++ headers, then libraries (Poco, etc) and, finally,
project headers come last.
This makes sure that headers and included in the same dependency
order to avoid side-effects. For example, Poco should never rely on
anything from our project in the same way that a C header should
never rely on anything in C++, Poco, or project headers.
Also, includes ought to be sorted where possible, to improve
readability and avoid accidental duplicates (of which there
were a few).
Change-Id: I62cc1343e4a091d69195e37ed659dba20cfcb1ef
Reviewed-on: https://gerrit.libreoffice.org/25262
Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
Tested-by: Ashod Nakashian <ashnakash@gmail.com>
2016-05-21 09:23:07 -05:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
2019-11-06 03:07:32 -06:00
|
|
|
#include <sysexits.h>
|
2016-04-05 08:32:10 -05:00
|
|
|
|
2016-04-18 06:02:36 -05:00
|
|
|
#include <atomic>
|
2016-04-05 08:32:10 -05:00
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
|
|
|
#include <iostream>
|
loolwsd: include cleanup and organization
A source file (.cpp) must include its own header first.
This insures that the header is self-contained and
doesn't depend on arbitrary (and accidental) includes
before it to compile.
Furthermore, system headers should go next, followed by
C then C++ headers, then libraries (Poco, etc) and, finally,
project headers come last.
This makes sure that headers and included in the same dependency
order to avoid side-effects. For example, Poco should never rely on
anything from our project in the same way that a C header should
never rely on anything in C++, Poco, or project headers.
Also, includes ought to be sorted where possible, to improve
readability and avoid accidental duplicates (of which there
were a few).
Change-Id: I62cc1343e4a091d69195e37ed659dba20cfcb1ef
Reviewed-on: https://gerrit.libreoffice.org/25262
Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
Tested-by: Ashod Nakashian <ashnakash@gmail.com>
2016-05-21 09:23:07 -05:00
|
|
|
#include <map>
|
2019-11-19 02:30:30 -06:00
|
|
|
#include <thread>
|
|
|
|
#include <chrono>
|
2016-04-05 08:32:10 -05:00
|
|
|
|
|
|
|
#include <Poco/Path.h>
|
|
|
|
|
2017-12-20 07:06:26 -06:00
|
|
|
#include <Common.hpp>
|
2016-11-24 08:56:06 -06:00
|
|
|
#include "Kit.hpp"
|
2020-07-02 08:57:22 -05:00
|
|
|
#include "SetupKitEnvironment.hpp"
|
2017-12-20 07:06:26 -06:00
|
|
|
#include <Log.hpp>
|
|
|
|
#include <Unit.hpp>
|
|
|
|
#include <Util.hpp>
|
2020-04-02 10:11:36 -05:00
|
|
|
#include <WebSocketHandler.hpp>
|
2020-04-07 16:21:17 -05:00
|
|
|
#if !MOBILEAPP
|
|
|
|
#include <Admin.hpp>
|
|
|
|
#endif
|
2017-12-20 07:06:26 -06:00
|
|
|
|
|
|
|
#include <common/FileUtil.hpp>
|
wsd: faster jail setup via bind-mount
loolmount now works and supports mounting and
unmounting, plus numerous improvements,
refactoring, logging, etc.. When enabled,
binding improves the jail setup time by anywhere
from 2x to orders of magnitude (in docker, f.e.).
A new config entry mount_jail_tree controls
whether mounting is used or the old method of
linking/copying of jail contents. It is set to
true by default and falls back to linking/copying.
A test mount is done when the setting is enabled,
and if mounting fails, it's disabled to avoid noise.
Temporarily disabled for unit-tests until we can
cleanup lingering mounts after Jenkins aborts our
build job. In a future patch we will have mount/jail
cleanup as part of make.
The network/system files in /etc that need frequent
refreshing are now updated in systemplate to make
their most recent version available in the jails.
These files can change during the course of loolwsd
lifetime, and are unlikely to be updated in
systemplate after installation at all. We link to
them in the systemplate/etc directory, and if that
fails, we copy them before forking each kit
instance to have the latest.
This reworks the approach used to bind-mount the
jails and the templates such that the total is
now down to only three mounts: systemplate, lo, tmp.
As now systemplate and lotemplate are shared, they
must be mounted as readonly, this means that user/
must now be moved into tmp/user/ which is writable.
The mount-points must be recursive, because we mount
lo/ within the mount-point of systemplate (which is
the root of the jail). But because we (re)bind
recursively, and because both systemplate and
lotemplate are mounted for each jails, we need to
make them unbindable, so they wouldn't multiply the
mount-points for each jails (an explosive growth!)
Contrarywise, we don't want the mount-points to
be shared, because we don't expect to add/remove
mounts after a jail is created.
The random temp directory is now created and set
correctly, plus many logging and other improvements.
Change-Id: Iae3fda5e876cf47d2cae6669a87b5b826a8748df
Reviewed-on: https://gerrit.libreoffice.org/c/online/+/92829
Tested-by: Jenkins
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com>
Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
2020-04-09 08:02:58 -05:00
|
|
|
#include <common/JailUtil.hpp>
|
2017-12-20 07:06:26 -06:00
|
|
|
#include <common/Seccomp.hpp>
|
|
|
|
#include <common/SigUtil.hpp>
|
|
|
|
#include <security.h>
|
2016-04-12 04:00:33 -05:00
|
|
|
|
2017-02-06 16:26:38 -06:00
|
|
|
#ifndef KIT_IN_PROCESS
|
2016-04-16 14:44:53 -05:00
|
|
|
static bool NoCapsForKit = false;
|
2018-03-19 10:20:10 -05:00
|
|
|
static bool NoSeccomp = false;
|
2020-03-04 13:38:17 -06:00
|
|
|
#if ENABLE_DEBUG
|
|
|
|
static bool SingleKit = false;
|
|
|
|
#endif
|
2017-02-06 16:26:38 -06:00
|
|
|
#endif
|
2020-03-04 13:38:17 -06:00
|
|
|
|
2020-06-26 05:58:09 -05:00
|
|
|
static std::string UserInterface;
|
|
|
|
|
2016-10-06 05:17:36 -05:00
|
|
|
static bool DisplayVersion = false;
|
2016-04-09 11:30:48 -05:00
|
|
|
static std::string UnitTestLibrary;
|
2018-01-07 21:34:28 -06:00
|
|
|
static std::string LogLevel;
|
2017-03-11 13:42:50 -06:00
|
|
|
static std::atomic<unsigned> ForkCounter(0);
|
2016-04-05 08:32:10 -05:00
|
|
|
|
2020-11-18 21:26:27 -06:00
|
|
|
/// The [child pid -> jail path] map.
|
2019-11-26 07:15:38 -06:00
|
|
|
static std::map<pid_t, std::string> childJails;
|
2020-11-18 21:26:27 -06:00
|
|
|
/// The jails that need cleaning up. This should be small.
|
|
|
|
static std::vector<std::string> cleanupJailPaths;
|
2016-04-18 06:02:36 -05:00
|
|
|
|
2017-02-06 08:59:14 -06:00
|
|
|
#ifndef KIT_IN_PROCESS
|
2016-04-14 12:04:19 -05:00
|
|
|
int ClientPortNumber = DEFAULT_CLIENT_PORT_NUMBER;
|
2019-03-30 09:06:16 -05:00
|
|
|
std::string MasterLocation;
|
2017-02-06 08:59:14 -06:00
|
|
|
#endif
|
2016-04-14 12:04:19 -05:00
|
|
|
|
2020-07-14 09:20:05 -05:00
|
|
|
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"
|
|
|
|
<< " unit test: " << UnitTestLibrary << "\n"
|
|
|
|
#ifndef KIT_IN_PROCESS
|
|
|
|
<< " NoCapsForKit: " << NoCapsForKit << "\n"
|
|
|
|
<< " NoSeccomp: " << NoSeccomp << "\n"
|
|
|
|
# if ENABLE_DEBUG
|
|
|
|
<< " SingleKit: " << SingleKit << "\n"
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
<< " ClientPortNumber: " << ClientPortNumber << "\n"
|
|
|
|
<< " MasterLocation: " << MasterLocation
|
|
|
|
<< "\n";
|
|
|
|
|
|
|
|
const std::string msg = oss.str();
|
|
|
|
fprintf(stderr, "%s", msg.c_str());
|
|
|
|
LOG_TRC(msg);
|
|
|
|
}
|
|
|
|
|
2020-04-02 10:11:36 -05:00
|
|
|
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
|
2016-04-05 08:32:10 -05:00
|
|
|
{
|
2020-04-02 10:11:36 -05:00
|
|
|
std::string _socketName;
|
|
|
|
|
2016-04-05 08:32:10 -05:00
|
|
|
public:
|
2020-04-02 10:11:36 -05:00
|
|
|
ServerWSHandler(const std::string& socketName) :
|
|
|
|
WebSocketHandler(/* isClient = */ true, /* isMasking */ false),
|
|
|
|
_socketName(socketName)
|
2016-04-05 08:32:10 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-04-02 10:11:36 -05:00
|
|
|
protected:
|
|
|
|
void handleMessage(const std::vector<char>& data) override
|
2016-04-05 08:32:10 -05:00
|
|
|
{
|
2020-04-02 10:11:36 -05:00
|
|
|
std::string message(data.data(), data.size());
|
|
|
|
|
|
|
|
#if !MOBILEAPP
|
|
|
|
if (UnitKit::get().filterKitMessage(this, message))
|
|
|
|
return;
|
|
|
|
#endif
|
2020-05-21 09:22:49 -05:00
|
|
|
StringVector tokens = Util::tokenize(message);
|
2020-04-02 10:11:36 -05:00
|
|
|
Log::StreamLogger logger = Log::debug();
|
|
|
|
if (logger.enabled())
|
2016-04-17 11:05:56 -05:00
|
|
|
{
|
2020-04-02 10:11:36 -05:00
|
|
|
logger << _socketName << ": recv [";
|
|
|
|
for (const auto& token : tokens)
|
2016-04-17 11:05:56 -05:00
|
|
|
{
|
2020-04-02 10:11:36 -05:00
|
|
|
logger << tokens.getParam(token) << ' ';
|
2016-04-17 11:05:56 -05:00
|
|
|
}
|
|
|
|
|
2020-04-02 10:11:36 -05:00
|
|
|
LOG_END(logger, true);
|
2016-04-17 11:05:56 -05:00
|
|
|
}
|
|
|
|
|
2020-04-02 10:11:36 -05:00
|
|
|
// Note: Syntax or parsing errors here are unexpected and fatal.
|
|
|
|
if (SigUtil::getTerminationFlag())
|
2016-04-05 08:32:10 -05:00
|
|
|
{
|
2020-04-02 10:11:36 -05:00
|
|
|
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)
|
2016-04-09 15:53:33 -05:00
|
|
|
{
|
2020-04-02 10:11:36 -05:00
|
|
|
LOG_INF("Setting to spawn " << tokens[1] << " child" << (count == 1 ? "" : "ren") << " per request.");
|
|
|
|
ForkCounter = count;
|
2016-04-09 15:53:33 -05:00
|
|
|
}
|
2020-04-02 10:11:36 -05:00
|
|
|
else
|
2017-06-11 10:54:46 -05:00
|
|
|
{
|
2020-04-02 10:11:36 -05:00
|
|
|
LOG_WRN("Cannot spawn " << tokens[1] << " children as requested.");
|
2017-06-11 10:54:46 -05:00
|
|
|
}
|
2020-11-15 06:23:54 -06:00
|
|
|
}
|
2020-11-23 05:07:44 -06:00
|
|
|
else if (tokens.size() == 2 && tokens.equals(0, "setloglevel"))
|
2020-11-15 06:23:54 -06:00
|
|
|
{
|
|
|
|
// Set environment variable so that new children will also set their log levels accordingly.
|
2020-11-30 02:58:57 -06:00
|
|
|
setenv("LOOL_LOGLEVEL", tokens[1].c_str(), 1);
|
2020-11-23 05:07:44 -06:00
|
|
|
Log::logger().setLevel(tokens[1]);
|
2020-04-02 10:11:36 -05:00
|
|
|
}
|
|
|
|
else if (tokens.size() == 3 && tokens.equals(0, "setconfig"))
|
|
|
|
{
|
|
|
|
// Currently only rlimit entries are supported.
|
|
|
|
if (!Rlimit::handleSetrlimitCommand(tokens))
|
2017-06-11 10:54:46 -05:00
|
|
|
{
|
2020-04-02 10:11:36 -05:00
|
|
|
LOG_ERR("Unknown setconfig command: " << message);
|
2017-06-11 10:54:46 -05:00
|
|
|
}
|
2016-04-05 08:32:10 -05:00
|
|
|
}
|
2020-04-02 10:11:36 -05:00
|
|
|
else if (tokens.equals(0, "exit"))
|
|
|
|
{
|
|
|
|
LOG_INF("Setting TerminationFlag due to 'exit' command from parent.");
|
|
|
|
SigUtil::setTerminationFlag();
|
|
|
|
}
|
|
|
|
else
|
2017-05-06 21:13:02 -05:00
|
|
|
{
|
2020-05-24 08:10:18 -05:00
|
|
|
LOG_ERR("Bad or unknown token [" << tokens[0] << ']');
|
2017-05-06 21:13:02 -05:00
|
|
|
}
|
2020-04-02 10:11:36 -05:00
|
|
|
}
|
2016-08-13 22:59:10 -05:00
|
|
|
|
2020-04-02 10:11:36 -05:00
|
|
|
void onDisconnect() override
|
|
|
|
{
|
|
|
|
#if !MOBILEAPP
|
|
|
|
LOG_WRN("ForKit connection lost without exit arriving from wsd. Setting TerminationFlag");
|
|
|
|
SigUtil::setTerminationFlag();
|
|
|
|
#endif
|
2016-04-05 08:32:10 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-02-06 16:26:38 -06:00
|
|
|
#ifndef KIT_IN_PROCESS
|
2020-11-08 09:24:32 -06:00
|
|
|
#ifndef __FreeBSD__
|
2016-10-12 06:19:42 -05:00
|
|
|
static bool haveCapability(cap_value_t capability)
|
|
|
|
{
|
|
|
|
cap_t caps = cap_get_proc();
|
|
|
|
|
|
|
|
if (caps == nullptr)
|
|
|
|
{
|
2016-11-13 10:59:34 -06:00
|
|
|
LOG_SFL("cap_get_proc() failed.");
|
2016-10-12 06:19:42 -05:00
|
|
|
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)
|
|
|
|
{
|
2020-05-24 08:10:18 -05:00
|
|
|
LOG_SFL("cap_get_flag failed for " << cap_name << '.');
|
2016-10-12 06:19:42 -05:00
|
|
|
cap_free(cap_name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-05-24 08:10:18 -05:00
|
|
|
LOG_SFL("cap_get_flag failed for capability " << capability << '.');
|
2016-10-12 06:19:42 -05:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value != CAP_SET)
|
|
|
|
{
|
|
|
|
if (cap_name)
|
|
|
|
{
|
2016-11-14 21:20:28 -06:00
|
|
|
LOG_FTL("Capability " << cap_name << " is not set for the loolforkit program.");
|
2016-10-12 06:19:42 -05:00
|
|
|
cap_free(cap_name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-11-14 21:20:28 -06:00
|
|
|
LOG_ERR("Capability " << capability << " is not set for the loolforkit program.");
|
2016-10-12 06:19:42 -05:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cap_name)
|
|
|
|
{
|
2016-11-14 21:20:28 -06:00
|
|
|
LOG_INF("Have capability " << cap_name);
|
2016-10-12 06:19:42 -05:00
|
|
|
cap_free(cap_name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-11-14 21:20:28 -06:00
|
|
|
LOG_INF("Have capability " << capability);
|
2016-10-12 06:19:42 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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_MKNOD))
|
|
|
|
result = false;
|
2016-10-12 07:15:39 -05:00
|
|
|
if (!haveCapability(CAP_FOWNER))
|
2016-10-12 06:19:42 -05:00
|
|
|
result = false;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2020-11-08 09:24:32 -06:00
|
|
|
#else
|
|
|
|
static bool haveCorrectCapabilities()
|
|
|
|
{
|
|
|
|
// chroot() can only be called by root
|
|
|
|
return getuid() == 0;
|
|
|
|
}
|
|
|
|
#endif // __FreeBSD__
|
2017-02-06 16:26:38 -06:00
|
|
|
#endif
|
2016-10-12 06:19:42 -05:00
|
|
|
|
2016-05-19 19:27:24 -05:00
|
|
|
/// Check if some previously forked kids have died.
|
|
|
|
static void cleanupChildren()
|
|
|
|
{
|
2019-11-26 07:15:38 -06:00
|
|
|
pid_t exitedChildPid;
|
2020-08-23 11:11:23 -05:00
|
|
|
int status = 0;
|
|
|
|
int segFaultCount = 0;
|
2019-11-12 03:50:33 -06:00
|
|
|
|
2017-05-07 10:05:34 -05:00
|
|
|
// Reap quickly without doing slow cleanup so WSD can spawn more rapidly.
|
2016-10-15 16:08:55 -05:00
|
|
|
while ((exitedChildPid = waitpid(-1, &status, WUNTRACED | WNOHANG)) > 0)
|
2016-05-19 19:27:24 -05:00
|
|
|
{
|
2017-01-29 19:19:23 -06:00
|
|
|
const auto it = childJails.find(exitedChildPid);
|
|
|
|
if (it != childJails.end())
|
2016-05-19 19:27:24 -05:00
|
|
|
{
|
2017-07-02 21:09:12 -05:00
|
|
|
LOG_INF("Child " << exitedChildPid << " has exited, will remove its jail [" << it->second << "].");
|
2020-11-18 21:26:27 -06:00
|
|
|
cleanupJailPaths.emplace_back(it->second);
|
2017-01-29 19:19:23 -06:00
|
|
|
childJails.erase(it);
|
2019-08-08 02:10:59 -05:00
|
|
|
if (childJails.empty() && !SigUtil::getTerminationFlag())
|
2018-06-03 16:54:50 -05:00
|
|
|
{
|
|
|
|
// We ran out of kits and we aren't terminating.
|
|
|
|
LOG_WRN("No live Kits exist, and we are not terminating yet.");
|
|
|
|
}
|
2020-04-07 16:21:17 -05:00
|
|
|
|
|
|
|
if (WIFSIGNALED(status) && (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGBUS))
|
|
|
|
{
|
2020-08-23 11:11:23 -05:00
|
|
|
++segFaultCount;
|
2020-04-07 16:21:17 -05:00
|
|
|
}
|
2016-05-19 19:27:24 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-11-14 21:20:28 -06:00
|
|
|
LOG_ERR("Unknown child " << exitedChildPid << " has exited");
|
2016-05-19 19:27:24 -05:00
|
|
|
}
|
|
|
|
}
|
2020-04-07 16:21:17 -05:00
|
|
|
|
|
|
|
if (segFaultCount)
|
|
|
|
{
|
|
|
|
#ifdef KIT_IN_PROCESS
|
|
|
|
#if !MOBILEAPP
|
|
|
|
Admin::instance().addSegFaultCount(segFaultCount);
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
if (WSHandler)
|
|
|
|
{
|
|
|
|
std::stringstream stream;
|
2020-05-24 08:10:18 -05:00
|
|
|
stream << "segfaultcount " << segFaultCount << '\n';
|
2020-04-07 16:21:17 -05:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-01-29 19:19:23 -06:00
|
|
|
// Now delete the jails.
|
2020-11-18 21:26:27 -06:00
|
|
|
auto i = cleanupJailPaths.size();
|
|
|
|
while (i-- > 0)
|
2020-08-23 11:11:23 -05:00
|
|
|
{
|
2020-11-18 21:26:27 -06:00
|
|
|
const std::string path = cleanupJailPaths[i];
|
2020-08-23 11:11:23 -05:00
|
|
|
JailUtil::removeJail(path);
|
2020-11-18 21:26:27 -06:00
|
|
|
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);
|
2020-08-23 11:11:23 -05:00
|
|
|
}
|
2016-05-19 19:27:24 -05:00
|
|
|
}
|
|
|
|
|
2016-04-05 08:32:10 -05:00
|
|
|
static int createLibreOfficeKit(const std::string& childRoot,
|
|
|
|
const std::string& sysTemplate,
|
|
|
|
const std::string& loTemplate,
|
2016-06-20 13:58:00 -05:00
|
|
|
const std::string& loSubPath,
|
|
|
|
bool queryVersion = false)
|
2016-04-05 08:32:10 -05:00
|
|
|
{
|
2017-05-07 10:05:34 -05:00
|
|
|
// Generate a jail ID to be used for in the jail path.
|
|
|
|
const std::string jailId = Util::rng::getFilename(16);
|
|
|
|
|
wsd: faster jail setup via bind-mount
loolmount now works and supports mounting and
unmounting, plus numerous improvements,
refactoring, logging, etc.. When enabled,
binding improves the jail setup time by anywhere
from 2x to orders of magnitude (in docker, f.e.).
A new config entry mount_jail_tree controls
whether mounting is used or the old method of
linking/copying of jail contents. It is set to
true by default and falls back to linking/copying.
A test mount is done when the setting is enabled,
and if mounting fails, it's disabled to avoid noise.
Temporarily disabled for unit-tests until we can
cleanup lingering mounts after Jenkins aborts our
build job. In a future patch we will have mount/jail
cleanup as part of make.
The network/system files in /etc that need frequent
refreshing are now updated in systemplate to make
their most recent version available in the jails.
These files can change during the course of loolwsd
lifetime, and are unlikely to be updated in
systemplate after installation at all. We link to
them in the systemplate/etc directory, and if that
fails, we copy them before forking each kit
instance to have the latest.
This reworks the approach used to bind-mount the
jails and the templates such that the total is
now down to only three mounts: systemplate, lo, tmp.
As now systemplate and lotemplate are shared, they
must be mounted as readonly, this means that user/
must now be moved into tmp/user/ which is writable.
The mount-points must be recursive, because we mount
lo/ within the mount-point of systemplate (which is
the root of the jail). But because we (re)bind
recursively, and because both systemplate and
lotemplate are mounted for each jails, we need to
make them unbindable, so they wouldn't multiply the
mount-points for each jails (an explosive growth!)
Contrarywise, we don't want the mount-points to
be shared, because we don't expect to add/remove
mounts after a jail is created.
The random temp directory is now created and set
correctly, plus many logging and other improvements.
Change-Id: Iae3fda5e876cf47d2cae6669a87b5b826a8748df
Reviewed-on: https://gerrit.libreoffice.org/c/online/+/92829
Tested-by: Jenkins
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com>
Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
2020-04-09 08:02:58 -05:00
|
|
|
// Update the dynamic files as necessary.
|
|
|
|
JailUtil::SysTemplate::updateDynamicFiles(sysTemplate);
|
|
|
|
|
2019-09-21 13:39:32 -05:00
|
|
|
// Used to label the spare kit instances
|
|
|
|
static size_t spareKitId = 0;
|
|
|
|
++spareKitId;
|
2020-12-13 21:06:17 -06:00
|
|
|
LOG_DBG("Forking a loolkit process with jailId: " << jailId << " as spare loolkit #"
|
|
|
|
<< spareKitId << '.');
|
2016-04-05 08:32:10 -05:00
|
|
|
|
2019-11-26 07:15:38 -06:00
|
|
|
const pid_t pid = fork();
|
2017-01-08 14:17:47 -06:00
|
|
|
if (!pid)
|
2016-04-05 08:32:10 -05:00
|
|
|
{
|
2016-10-13 05:10:09 -05:00
|
|
|
// Child
|
|
|
|
|
2016-10-12 10:15:13 -05:00
|
|
|
// Close the pipe from loolwsd
|
|
|
|
close(0);
|
2016-04-05 08:51:22 -05:00
|
|
|
|
2017-02-06 16:26:38 -06:00
|
|
|
#ifndef KIT_IN_PROCESS
|
2016-04-09 12:26:33 -05:00
|
|
|
UnitKit::get().postFork();
|
2017-02-06 16:26:38 -06:00
|
|
|
#endif
|
2016-04-09 12:26:33 -05:00
|
|
|
|
2016-04-05 08:32:10 -05:00
|
|
|
if (std::getenv("SLEEPKITFORDEBUGGER"))
|
|
|
|
{
|
2018-02-07 03:17:28 -06:00
|
|
|
const size_t delaySecs = std::stoul(std::getenv("SLEEPKITFORDEBUGGER"));
|
2016-10-22 18:04:07 -05:00
|
|
|
if (delaySecs > 0)
|
|
|
|
{
|
2018-06-03 16:54:50 -05:00
|
|
|
std::cerr << "Kit: Sleeping " << delaySecs
|
2016-10-22 18:04:07 -05:00
|
|
|
<< " seconds to give you time to attach debugger to process "
|
2019-11-26 07:15:38 -06:00
|
|
|
<< getpid() << std::endl;
|
2019-11-19 02:30:30 -06:00
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(delaySecs));
|
2016-10-22 18:04:07 -05:00
|
|
|
}
|
2016-04-05 08:32:10 -05:00
|
|
|
}
|
|
|
|
|
2017-02-06 16:26:38 -06:00
|
|
|
#ifndef KIT_IN_PROCESS
|
2019-09-21 13:39:32 -05:00
|
|
|
lokit_main(childRoot, jailId, sysTemplate, loTemplate, loSubPath, NoCapsForKit, NoSeccomp, queryVersion, DisplayVersion, spareKitId);
|
2017-02-06 16:26:38 -06:00
|
|
|
#else
|
2019-09-21 13:39:32 -05:00
|
|
|
lokit_main(childRoot, jailId, sysTemplate, loTemplate, loSubPath, true, true, queryVersion, DisplayVersion, spareKitId);
|
2017-02-06 16:26:38 -06:00
|
|
|
#endif
|
2016-04-05 08:32:10 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-04-18 06:02:36 -05:00
|
|
|
// Parent
|
2016-04-07 02:36:38 -05:00
|
|
|
if (pid < 0)
|
2016-05-19 19:27:24 -05:00
|
|
|
{
|
2016-11-13 10:59:34 -06:00
|
|
|
LOG_SYS("Fork failed.");
|
2016-05-19 19:27:24 -05:00
|
|
|
}
|
2016-04-07 02:36:38 -05:00
|
|
|
else
|
2016-04-18 06:02:36 -05:00
|
|
|
{
|
2016-11-14 21:20:28 -06:00
|
|
|
LOG_INF("Forked kit [" << pid << "].");
|
2017-05-07 10:05:34 -05:00
|
|
|
childJails[pid] = childRoot + jailId;
|
2016-04-18 06:02:36 -05:00
|
|
|
}
|
2016-04-13 09:10:02 -05:00
|
|
|
|
2017-02-06 16:26:38 -06:00
|
|
|
#ifndef KIT_IN_PROCESS
|
2016-04-13 09:10:02 -05:00
|
|
|
UnitKit::get().launchedKit(pid);
|
2017-02-06 16:26:38 -06:00
|
|
|
#endif
|
2016-04-05 08:32:10 -05:00
|
|
|
}
|
|
|
|
|
2016-04-08 05:09:06 -05:00
|
|
|
return pid;
|
2016-04-05 08:32:10 -05:00
|
|
|
}
|
|
|
|
|
2017-02-06 16:26:38 -06:00
|
|
|
void forkLibreOfficeKit(const std::string& childRoot,
|
|
|
|
const std::string& sysTemplate,
|
|
|
|
const std::string& loTemplate,
|
|
|
|
const std::string& loSubPath,
|
|
|
|
int limit)
|
|
|
|
{
|
|
|
|
// Cleanup first, to reduce disk load.
|
|
|
|
cleanupChildren();
|
|
|
|
|
|
|
|
#ifndef KIT_IN_PROCESS
|
|
|
|
(void) limit;
|
|
|
|
#else
|
|
|
|
if (limit > 0)
|
|
|
|
ForkCounter = limit;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
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, loSubPath) < 0)
|
|
|
|
{
|
|
|
|
LOG_ERR("Failed to create a kit process.");
|
|
|
|
++ForkCounter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef KIT_IN_PROCESS
|
2016-04-05 08:32:10 -05:00
|
|
|
static void printArgumentHelp()
|
|
|
|
{
|
|
|
|
std::cout << "Usage: loolforkit [OPTION]..." << std::endl;
|
2016-04-07 03:27:43 -05:00
|
|
|
std::cout << " Single-threaded process that spawns lok instances" << std::endl;
|
|
|
|
std::cout << " Note: Running this standalone is not possible. It is spawned by loolwsd" << std::endl;
|
2016-04-05 08:32:10 -05:00
|
|
|
std::cout << " and is controlled via a pipe." << std::endl;
|
|
|
|
std::cout << "" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char** argv)
|
|
|
|
{
|
2020-12-18 08:47:51 -06:00
|
|
|
/*WARNING: PRIVILEGED CODE CHECKING START */
|
|
|
|
|
|
|
|
/*WARNING*/ // early check for avoiding the security check for username 'lool'
|
|
|
|
/*WARNING*/ // (deliberately only this, not moving the entire parameter parsing here)
|
|
|
|
/*WARNING*/ bool checkLoolUser = true;
|
|
|
|
/*WARNING*/ std::string disableLoolUserChecking("--disable-lool-user-checking");
|
|
|
|
/*WARNING*/ for (int i = 1; checkLoolUser && (i < argc); ++i)
|
|
|
|
/*WARNING*/ {
|
|
|
|
/*WARNING*/ if (disableLoolUserChecking == argv[i])
|
|
|
|
/*WARNING*/ checkLoolUser = false;
|
|
|
|
/*WARNING*/ }
|
|
|
|
|
|
|
|
/*WARNING*/ if (!hasCorrectUID("loolforkit"))
|
|
|
|
/*WARNING*/ {
|
|
|
|
/*WARNING*/ // don't allow if any capability is set (unless root; who runs this
|
|
|
|
/*WARNING*/ // as root and provides --disable-lool-user-checking knows what they
|
|
|
|
/*WARNING*/ // are doing)
|
|
|
|
/*WARNING*/ if (hasAnyCapability() && !hasUID("root"))
|
|
|
|
/*WARNING*/ {
|
|
|
|
/*WARNING*/ if (!checkLoolUser)
|
|
|
|
/*WARNING*/ std::cerr << "Security: --disable-lool-user-checking failed, loolforkit has some capabilities set." << std::endl;
|
|
|
|
|
|
|
|
/*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-lool-user-checking
|
|
|
|
/*WARNING*/ if (checkLoolUser)
|
|
|
|
/*WARNING*/ return EX_SOFTWARE;
|
|
|
|
|
|
|
|
/*WARNING*/ std::cerr << "Security: Check for the 'lool' username overridden on the command line." << std::endl;
|
|
|
|
/*WARNING*/ }
|
|
|
|
|
|
|
|
/*WARNING: PRIVILEGED CODE CHECKING END */
|
|
|
|
|
|
|
|
// Continue in privileged mode, but only if:
|
|
|
|
// * the user is 'lool' (privileged user)
|
|
|
|
// * the user is 'root', and --disable-lool-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-lool-user-checking was provided
|
2016-04-12 04:00:33 -05:00
|
|
|
|
2016-04-05 08:32:10 -05:00
|
|
|
if (std::getenv("SLEEPFORDEBUGGER"))
|
|
|
|
{
|
2018-02-07 03:17:28 -06:00
|
|
|
const size_t delaySecs = std::stoul(std::getenv("SLEEPFORDEBUGGER"));
|
2016-10-22 18:04:07 -05:00
|
|
|
if (delaySecs > 0)
|
|
|
|
{
|
2018-06-03 16:54:50 -05:00
|
|
|
std::cerr << "Forkit: Sleeping " << delaySecs
|
2016-10-22 18:04:07 -05:00
|
|
|
<< " seconds to give you time to attach debugger to process "
|
2019-11-26 07:15:38 -06:00
|
|
|
<< getpid() << std::endl;
|
2019-11-19 02:30:30 -06:00
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(delaySecs));
|
2016-10-22 18:04:07 -05:00
|
|
|
}
|
2016-04-05 08:32:10 -05:00
|
|
|
}
|
|
|
|
|
2017-02-08 14:39:55 -06:00
|
|
|
#ifndef FUZZER
|
2016-11-14 07:58:04 -06:00
|
|
|
SigUtil::setFatalSignals();
|
|
|
|
SigUtil::setTerminationSignals();
|
2017-02-08 14:39:55 -06:00
|
|
|
#endif
|
2016-11-14 07:58:04 -06:00
|
|
|
|
2017-03-30 15:55:17 -05:00
|
|
|
Util::setThreadName("forkit");
|
wsd: faster jail setup via bind-mount
loolmount now works and supports mounting and
unmounting, plus numerous improvements,
refactoring, logging, etc.. When enabled,
binding improves the jail setup time by anywhere
from 2x to orders of magnitude (in docker, f.e.).
A new config entry mount_jail_tree controls
whether mounting is used or the old method of
linking/copying of jail contents. It is set to
true by default and falls back to linking/copying.
A test mount is done when the setting is enabled,
and if mounting fails, it's disabled to avoid noise.
Temporarily disabled for unit-tests until we can
cleanup lingering mounts after Jenkins aborts our
build job. In a future patch we will have mount/jail
cleanup as part of make.
The network/system files in /etc that need frequent
refreshing are now updated in systemplate to make
their most recent version available in the jails.
These files can change during the course of loolwsd
lifetime, and are unlikely to be updated in
systemplate after installation at all. We link to
them in the systemplate/etc directory, and if that
fails, we copy them before forking each kit
instance to have the latest.
This reworks the approach used to bind-mount the
jails and the templates such that the total is
now down to only three mounts: systemplate, lo, tmp.
As now systemplate and lotemplate are shared, they
must be mounted as readonly, this means that user/
must now be moved into tmp/user/ which is writable.
The mount-points must be recursive, because we mount
lo/ within the mount-point of systemplate (which is
the root of the jail). But because we (re)bind
recursively, and because both systemplate and
lotemplate are mounted for each jails, we need to
make them unbindable, so they wouldn't multiply the
mount-points for each jails (an explosive growth!)
Contrarywise, we don't want the mount-points to
be shared, because we don't expect to add/remove
mounts after a jail is created.
The random temp directory is now created and set
correctly, plus many logging and other improvements.
Change-Id: Iae3fda5e876cf47d2cae6669a87b5b826a8748df
Reviewed-on: https://gerrit.libreoffice.org/c/online/+/92829
Tested-by: Jenkins
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com>
Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
2020-04-09 08:02:58 -05:00
|
|
|
Util::setApplicationPath(Poco::Path(argv[0]).parent().toString());
|
2017-03-30 15:55:17 -05:00
|
|
|
|
2016-04-05 08:32:10 -05:00
|
|
|
// Initialization
|
2016-10-12 03:47:26 -05:00
|
|
|
const bool logToFile = std::getenv("LOOL_LOGFILE");
|
|
|
|
const char* logFilename = std::getenv("LOOL_LOGFILENAME");
|
|
|
|
const char* logLevel = std::getenv("LOOL_LOGLEVEL");
|
|
|
|
const char* logColor = std::getenv("LOOL_LOGCOLOR");
|
2016-09-27 07:30:45 -05:00
|
|
|
std::map<std::string, std::string> logProperties;
|
|
|
|
if (logToFile && logFilename)
|
|
|
|
{
|
|
|
|
logProperties["path"] = std::string(logFilename);
|
|
|
|
}
|
|
|
|
|
2018-01-07 21:34:28 -06:00
|
|
|
Log::initialize("frk", "trace", logColor != nullptr, logToFile, logProperties);
|
|
|
|
LogLevel = logLevel ? logLevel : "trace";
|
|
|
|
if (LogLevel != "trace")
|
|
|
|
{
|
2018-06-03 20:01:47 -05:00
|
|
|
LOG_INF("Setting log-level to [trace] and delaying setting to configured [" << LogLevel << "] until after Forkit initialization.");
|
2018-01-07 21:34:28 -06:00
|
|
|
}
|
2016-04-05 08:32:10 -05:00
|
|
|
|
|
|
|
std::string childRoot;
|
|
|
|
std::string loSubPath;
|
|
|
|
std::string sysTemplate;
|
|
|
|
std::string loTemplate;
|
|
|
|
|
|
|
|
for (int i = 0; i < argc; ++i)
|
|
|
|
{
|
|
|
|
char *cmd = argv[i];
|
|
|
|
char *eq;
|
|
|
|
if (std::strstr(cmd, "--losubpath=") == cmd)
|
|
|
|
{
|
|
|
|
eq = std::strchr(cmd, '=');
|
|
|
|
loSubPath = std::string(eq+1);
|
|
|
|
}
|
|
|
|
else 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));
|
|
|
|
}
|
2016-10-26 08:40:46 -05:00
|
|
|
else if (std::strstr(cmd, "--masterport=") == cmd)
|
|
|
|
{
|
|
|
|
eq = std::strchr(cmd, '=');
|
2019-03-30 09:06:16 -05:00
|
|
|
MasterLocation = std::string(eq+1);
|
2016-10-26 08:40:46 -05:00
|
|
|
}
|
2016-04-15 09:07:24 -05:00
|
|
|
else if (std::strstr(cmd, "--version") == cmd)
|
|
|
|
{
|
2016-06-20 04:51:35 -05:00
|
|
|
std::string version, hash;
|
|
|
|
Util::getVersionInfo(version, hash);
|
2016-10-06 05:17:36 -05:00
|
|
|
std::cout << "loolforkit version details: " << version << " - " << hash << std::endl;
|
|
|
|
DisplayVersion = true;
|
2016-04-15 09:07:24 -05:00
|
|
|
}
|
2017-06-11 10:48:24 -05:00
|
|
|
else if (std::strstr(cmd, "--rlimits") == cmd)
|
|
|
|
{
|
|
|
|
eq = std::strchr(cmd, '=');
|
|
|
|
const std::string rlimits = std::string(eq+1);
|
2020-05-21 09:22:49 -05:00
|
|
|
StringVector tokens = Util::tokenize(rlimits, ';');
|
2020-02-28 07:51:22 -06:00
|
|
|
for (const auto& cmdLimit : tokens)
|
2017-06-11 10:48:24 -05:00
|
|
|
{
|
2020-02-28 07:51:22 -06:00
|
|
|
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);
|
2017-06-30 05:10:38 -05:00
|
|
|
if (!Rlimit::handleSetrlimitCommand(tokensLimit))
|
2017-06-11 10:48:24 -05:00
|
|
|
{
|
2020-02-28 07:51:22 -06:00
|
|
|
LOG_ERR("Unknown rlimits command: " << tokens.getParam(cmdLimit));
|
2017-06-11 10:48:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-04-12 04:00:33 -05:00
|
|
|
#if ENABLE_DEBUG
|
|
|
|
// this process has various privileges - don't run arbitrary code.
|
2016-04-09 11:30:48 -05:00
|
|
|
else if (std::strstr(cmd, "--unitlib=") == cmd)
|
|
|
|
{
|
|
|
|
eq = std::strchr(cmd, '=');
|
|
|
|
UnitTestLibrary = std::string(eq+1);
|
|
|
|
}
|
2020-03-04 13:38:17 -06:00
|
|
|
else if (std::strstr(cmd, "--singlekit") == cmd)
|
|
|
|
{
|
|
|
|
SingleKit = true;
|
|
|
|
}
|
2018-03-19 10:20:10 -05:00
|
|
|
#endif
|
|
|
|
// we are running in a lower-privilege mode - with no chroot
|
2016-04-16 14:44:53 -05:00
|
|
|
else if (std::strstr(cmd, "--nocaps") == cmd)
|
|
|
|
{
|
2018-03-19 10:20:10 -05:00
|
|
|
LOG_ERR("Security: Running without the capability to enter a chroot jail is ill advised.");
|
2016-04-16 14:44:53 -05:00
|
|
|
NoCapsForKit = true;
|
|
|
|
}
|
2018-03-19 10:20:10 -05:00
|
|
|
|
|
|
|
// we are running without seccomp protection
|
|
|
|
else if (std::strstr(cmd, "--noseccomp") == cmd)
|
|
|
|
{
|
2020-04-23 13:01:04 -05:00
|
|
|
LOG_ERR("Security: Running without the ability to filter system calls is ill advised.");
|
2018-03-19 10:20:10 -05:00
|
|
|
NoSeccomp = true;
|
|
|
|
}
|
2020-06-26 05:58:09 -05:00
|
|
|
|
|
|
|
else if (std::strstr(cmd, "--ui") == cmd)
|
|
|
|
{
|
|
|
|
eq = std::strchr(cmd, '=');
|
|
|
|
UserInterface = std::string(eq+1);
|
|
|
|
}
|
2016-04-05 08:32:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (loSubPath.empty() || sysTemplate.empty() ||
|
|
|
|
loTemplate.empty() || childRoot.empty())
|
|
|
|
{
|
|
|
|
printArgumentHelp();
|
2019-11-06 03:07:32 -06:00
|
|
|
return EX_USAGE;
|
2016-04-05 08:32:10 -05:00
|
|
|
}
|
|
|
|
|
2016-12-22 03:27:30 -06:00
|
|
|
if (!UnitBase::init(UnitBase::UnitType::Kit,
|
2016-04-09 11:30:48 -05:00
|
|
|
UnitTestLibrary))
|
|
|
|
{
|
2016-11-14 21:20:28 -06:00
|
|
|
LOG_ERR("Failed to load kit unit test library");
|
2019-11-06 03:07:32 -06:00
|
|
|
return EX_USAGE;
|
2016-04-09 11:30:48 -05:00
|
|
|
}
|
|
|
|
|
2020-06-26 05:58:09 -05:00
|
|
|
setupKitEnvironment(UserInterface);
|
2017-03-11 13:43:26 -06:00
|
|
|
|
|
|
|
if (!std::getenv("LD_BIND_NOW")) // must be set by parent.
|
2016-11-14 21:20:28 -06:00
|
|
|
LOG_INF("Note: LD_BIND_NOW is not set.");
|
2016-04-05 08:32:10 -05:00
|
|
|
|
2017-02-08 22:19:29 -06:00
|
|
|
if (!NoCapsForKit && !haveCorrectCapabilities())
|
2017-08-10 04:11:05 -05:00
|
|
|
{
|
|
|
|
std::cerr << "FATAL: Capabilities are not set for the loolforkit program." << std::endl;
|
2018-01-31 04:45:03 -06:00
|
|
|
std::cerr << "Please make sure that the current partition was *not* mounted with the 'nosuid' option." << std::endl;
|
2017-08-10 04:11:05 -05:00
|
|
|
std::cerr << "If you are on SLES11, please set 'file_caps=1' as kernel boot option." << std::endl << std::endl;
|
2019-11-06 03:07:32 -06:00
|
|
|
return EX_SOFTWARE;
|
2017-08-10 04:11:05 -05:00
|
|
|
}
|
2016-10-12 06:49:01 -05:00
|
|
|
|
2016-04-05 08:32:10 -05:00
|
|
|
// Initialize LoKit
|
|
|
|
if (!globalPreinit(loTemplate))
|
2018-07-16 20:42:17 -05:00
|
|
|
{
|
|
|
|
LOG_FTL("Failed to preinit lokit.");
|
|
|
|
Log::shutdown();
|
2019-11-06 03:07:32 -06:00
|
|
|
std::_Exit(EX_SOFTWARE);
|
2018-07-16 20:42:17 -05:00
|
|
|
}
|
2016-04-05 08:32:10 -05:00
|
|
|
|
2018-04-16 14:03:01 -05:00
|
|
|
if (Util::getProcessThreadCount() != 1)
|
|
|
|
LOG_ERR("Error: forkit has more than a single thread after pre-init");
|
|
|
|
|
2020-08-23 11:11:23 -05:00
|
|
|
// Link the network and system files in sysTemplate, if possible.
|
wsd: faster jail setup via bind-mount
loolmount now works and supports mounting and
unmounting, plus numerous improvements,
refactoring, logging, etc.. When enabled,
binding improves the jail setup time by anywhere
from 2x to orders of magnitude (in docker, f.e.).
A new config entry mount_jail_tree controls
whether mounting is used or the old method of
linking/copying of jail contents. It is set to
true by default and falls back to linking/copying.
A test mount is done when the setting is enabled,
and if mounting fails, it's disabled to avoid noise.
Temporarily disabled for unit-tests until we can
cleanup lingering mounts after Jenkins aborts our
build job. In a future patch we will have mount/jail
cleanup as part of make.
The network/system files in /etc that need frequent
refreshing are now updated in systemplate to make
their most recent version available in the jails.
These files can change during the course of loolwsd
lifetime, and are unlikely to be updated in
systemplate after installation at all. We link to
them in the systemplate/etc directory, and if that
fails, we copy them before forking each kit
instance to have the latest.
This reworks the approach used to bind-mount the
jails and the templates such that the total is
now down to only three mounts: systemplate, lo, tmp.
As now systemplate and lotemplate are shared, they
must be mounted as readonly, this means that user/
must now be moved into tmp/user/ which is writable.
The mount-points must be recursive, because we mount
lo/ within the mount-point of systemplate (which is
the root of the jail). But because we (re)bind
recursively, and because both systemplate and
lotemplate are mounted for each jails, we need to
make them unbindable, so they wouldn't multiply the
mount-points for each jails (an explosive growth!)
Contrarywise, we don't want the mount-points to
be shared, because we don't expect to add/remove
mounts after a jail is created.
The random temp directory is now created and set
correctly, plus many logging and other improvements.
Change-Id: Iae3fda5e876cf47d2cae6669a87b5b826a8748df
Reviewed-on: https://gerrit.libreoffice.org/c/online/+/92829
Tested-by: Jenkins
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com>
Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
2020-04-09 08:02:58 -05:00
|
|
|
JailUtil::SysTemplate::setupDynamicFiles(sysTemplate);
|
|
|
|
|
|
|
|
// Make dev/[u]random point to the writable devices in tmp/dev/.
|
|
|
|
JailUtil::SysTemplate::setupRandomDeviceLinks(sysTemplate);
|
|
|
|
|
2016-11-14 21:20:28 -06:00
|
|
|
LOG_INF("Preinit stage OK.");
|
2016-04-05 08:32:10 -05:00
|
|
|
|
|
|
|
// We must have at least one child, more are created dynamically.
|
2018-01-08 22:56:15 -06:00
|
|
|
// Ask this first child to send version information to master process and trace startup.
|
|
|
|
::setenv("LOOL_TRACE_STARTUP", "1", 1);
|
2019-11-26 07:15:38 -06:00
|
|
|
pid_t forKitPid = createLibreOfficeKit(childRoot, sysTemplate, loTemplate, loSubPath, true);
|
2016-12-16 20:25:02 -06:00
|
|
|
if (forKitPid < 0)
|
2016-04-05 08:32:10 -05:00
|
|
|
{
|
2016-12-19 17:28:26 -06:00
|
|
|
LOG_FTL("Failed to create a kit process.");
|
2018-07-16 20:42:17 -05:00
|
|
|
Log::shutdown();
|
2019-11-06 03:07:32 -06:00
|
|
|
std::_Exit(EX_SOFTWARE);
|
2016-04-05 08:32:10 -05:00
|
|
|
}
|
|
|
|
|
2018-01-08 22:56:15 -06:00
|
|
|
// No need to trace subsequent children.
|
|
|
|
::unsetenv("LOOL_TRACE_STARTUP");
|
|
|
|
if (LogLevel != "trace")
|
|
|
|
{
|
2018-06-03 20:01:47 -05:00
|
|
|
LOG_INF("Forkit initialization complete: setting log-level to [" << LogLevel << "] as configured.");
|
2018-01-08 22:56:15 -06:00
|
|
|
Log::logger().setLevel(LogLevel);
|
|
|
|
}
|
|
|
|
|
2020-04-02 10:11:36 -05:00
|
|
|
SocketPoll mainPoll(Util::getThreadName());
|
|
|
|
mainPoll.runOnClientThread(); // We will do the polling on this thread.
|
|
|
|
|
|
|
|
WSHandler = std::make_shared<ServerWSHandler>("forkit_ws");
|
|
|
|
|
|
|
|
#if !MOBILEAPP
|
|
|
|
mainPoll.insertNewUnixSocket(MasterLocation, FORKIT_URI, WSHandler);
|
|
|
|
#endif
|
|
|
|
|
2020-07-14 09:20:05 -05:00
|
|
|
SigUtil::setUserSignals();
|
|
|
|
|
2016-11-14 21:20:28 -06:00
|
|
|
LOG_INF("ForKit process is ready.");
|
2016-04-05 08:32:10 -05:00
|
|
|
|
2019-08-08 02:10:59 -05:00
|
|
|
while (!SigUtil::getTerminationFlag())
|
2016-04-05 08:32:10 -05:00
|
|
|
{
|
2016-04-09 12:26:33 -05:00
|
|
|
UnitKit::get().invokeForKitTest();
|
|
|
|
|
2020-04-10 06:34:15 -05:00
|
|
|
mainPoll.poll(POLL_TIMEOUT_MICRO_S);
|
2016-04-05 08:32:10 -05:00
|
|
|
|
2020-07-14 09:20:05 -05:00
|
|
|
SigUtil::checkDumpGlobalState(dump_forkit_state);
|
|
|
|
|
2020-03-04 13:38:17 -06:00
|
|
|
#if ENABLE_DEBUG
|
|
|
|
if (!SingleKit)
|
|
|
|
#endif
|
2017-02-06 16:26:38 -06:00
|
|
|
forkLibreOfficeKit(childRoot, sysTemplate, loTemplate, loSubPath);
|
2016-04-05 08:32:10 -05:00
|
|
|
}
|
|
|
|
|
2019-11-06 03:07:32 -06:00
|
|
|
int returnValue = EX_OK;
|
2016-04-09 11:30:48 -05:00
|
|
|
UnitKit::get().returnValue(returnValue);
|
|
|
|
|
2016-12-17 05:56:03 -06:00
|
|
|
#if 0
|
|
|
|
int status = 0;
|
|
|
|
waitpid(forKitPid, &status, WUNTRACED);
|
|
|
|
#endif
|
|
|
|
|
2016-11-14 21:20:28 -06:00
|
|
|
LOG_INF("ForKit process finished.");
|
2018-07-16 20:42:17 -05:00
|
|
|
Log::shutdown();
|
2016-04-17 11:02:32 -05:00
|
|
|
std::_Exit(returnValue);
|
2016-04-05 08:32:10 -05:00
|
|
|
}
|
2017-02-06 16:26:38 -06:00
|
|
|
#endif
|
2016-04-05 08:32:10 -05:00
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|