[Libreoffice-commits] online.git: loolwsd/LOOLSession.cpp loolwsd/LOOLSession.hpp loolwsd/LOOLWSD.cpp loolwsd/LOOLWSD.hpp
Henry Castro
hcastro at collabora.com
Mon Jul 13 07:18:05 PDT 2015
loolwsd/LOOLSession.cpp | 245 +++-------------------
loolwsd/LOOLSession.hpp | 7
loolwsd/LOOLWSD.cpp | 528 ++++++++++++++++++++++++++++++++++++------------
loolwsd/LOOLWSD.hpp | 14 +
4 files changed, 454 insertions(+), 340 deletions(-)
New commits:
commit bb3611399be37ba545421198ae0d7a2016fb45ea
Author: Henry Castro <hcastro at collabora.com>
Date: Mon Jul 13 10:13:06 2015 -0400
loolwsd: Use fork but no execve
diff --git a/loolwsd/LOOLSession.cpp b/loolwsd/LOOLSession.cpp
index 982a95f..0fdf4d8 100644
--- a/loolwsd/LOOLSession.cpp
+++ b/loolwsd/LOOLSession.cpp
@@ -43,6 +43,9 @@
#include <Poco/URIStreamOpener.h>
#include <Poco/Util/Application.h>
#include <Poco/Exception.h>
+#include <Poco/Net/NetException.h>
+#include <Poco/Net/DialogSocket.h>
+#include <Poco/Net/SocketAddress.h>
#include "LOKitHelper.hpp"
#include "LOOLProtocol.hpp"
@@ -70,6 +73,9 @@ using Poco::URI;
using Poco::URIStreamOpener;
using Poco::Util::Application;
using Poco::Exception;
+using Poco::Net::DialogSocket;
+using Poco::Net::SocketAddress;
+using Poco::Net::WebSocketException;
const std::string LOOLSession::jailDocumentURL = "/user/thedocument";
@@ -215,13 +221,22 @@ bool MasterProcessSession::handleInput(const char *buffer, int length)
sendTextFrame("error: cmd=child kind=syntax");
return false;
}
+
UInt64 childId = std::stoull(tokens[1]);
- if (_pendingPreSpawnedChildren.find(childId) == _pendingPreSpawnedChildren.end())
+ // TODO. rework, the desktop and its childrem is jail root same folder
+ /*if (_pendingPreSpawnedChildren.find(childId) == _pendingPreSpawnedChildren.end())
{
+ std::cout << Util::logPrefix() << "Error _pendingPreSpawnedChildren.find(childId)" << this << " id=" << childId << std::endl;
+
sendTextFrame("error: cmd=child kind=notfound");
return false;
+ }*/
+
+ if (_pendingPreSpawnedChildren.size() > 0)
+ {
+ std::set<UInt64>::iterator it = _pendingPreSpawnedChildren.begin();
+ _pendingPreSpawnedChildren.erase(it);
}
- _pendingPreSpawnedChildren.erase(childId);
std::unique_lock<std::mutex> lock(_availableChildSessionMutex);
_availableChildSessions.insert(shared_from_this());
std::cout << Util::logPrefix() << "Inserted " << this << " id=" << childId << " into _availableChildSessions, size=" << _availableChildSessions.size() << std::endl;
@@ -306,176 +321,22 @@ Path MasterProcessSession::getJailPath(UInt64 childId)
return Path::forDirectory(LOOLWSD::childRoot + Path::separator() + std::to_string(childId));
}
-namespace
+void MasterProcessSession::addPendingChildrem(UInt64 childId)
{
- ThreadLocal<std::string> sourceForLinkOrCopy;
- ThreadLocal<Path> destinationForLinkOrCopy;
-
- int linkOrCopyFunction(const char *fpath,
- const struct stat *sb,
- int typeflag,
- struct FTW *ftwbuf)
- {
- if (strcmp(fpath, sourceForLinkOrCopy->c_str()) == 0)
- return 0;
-
- assert(fpath[strlen(sourceForLinkOrCopy->c_str())] == '/');
- const char *relativeOldPath = fpath + strlen(sourceForLinkOrCopy->c_str()) + 1;
-
-#ifdef __APPLE__
- if (strcmp(relativeOldPath, "PkgInfo") == 0)
- return 0;
-#endif
-
- Path newPath(*destinationForLinkOrCopy, Path(relativeOldPath));
-
- switch (typeflag)
- {
- case FTW_F:
- File(newPath.parent()).createDirectories();
- if (link(fpath, newPath.toString().c_str()) == -1)
- {
- Application::instance().logger().error(Util::logPrefix() +
- "link(\"" + fpath + "\",\"" + newPath.toString() + "\") failed: " +
- strerror(errno));
- exit(1);
- }
- break;
- case FTW_DP:
- {
- struct stat st;
- if (stat(fpath, &st) == -1)
- {
- Application::instance().logger().error(Util::logPrefix() +
- "stat(\"" + fpath + "\") failed: " +
- strerror(errno));
- return 1;
- }
- File(newPath).createDirectories();
- struct utimbuf ut;
- ut.actime = st.st_atime;
- ut.modtime = st.st_mtime;
- if (utime(newPath.toString().c_str(), &ut) == -1)
- {
- Application::instance().logger().error(Util::logPrefix() +
- "utime(\"" + newPath.toString() + "\", &ut) failed: " +
- strerror(errno));
- return 1;
- }
- }
- break;
- case FTW_DNR:
- Application::instance().logger().error(Util::logPrefix() +
- "Cannot read directory '" + fpath + "'");
- return 1;
- case FTW_NS:
- Application::instance().logger().error(Util::logPrefix() +
- "nftw: stat failed for '" + fpath + "'");
- return 1;
- case FTW_SLN:
- Application::instance().logger().information(Util::logPrefix() +
- "nftw: symlink to nonexistent file: '" + fpath + "', ignored");
- break;
- default:
- assert(false);
- }
- return 0;
- }
-
- void linkOrCopy(const std::string& source, const Path& destination)
- {
- *sourceForLinkOrCopy = source;
- if (sourceForLinkOrCopy->back() == '/')
- sourceForLinkOrCopy->pop_back();
- *destinationForLinkOrCopy = destination;
- if (nftw(source.c_str(), linkOrCopyFunction, 10, FTW_DEPTH) == -1)
- Application::instance().logger().error(Util::logPrefix() +
- "linkOrCopy: nftw() failed for '" + source + "'");
- }
+ _pendingPreSpawnedChildren.insert(childId);
}
-void MasterProcessSession::preSpawn()
+int MasterProcessSession::getAvailableChildSessions()
{
- // Create child-specific subtree that will become its chroot root
-
- std::unique_lock<std::mutex> rngLock(_rngMutex);
- UInt64 childId = (((UInt64)_rng.next()) << 32) | _rng.next() | 1;
- rngLock.unlock();
-
- Path jail = getJailPath(childId);
- File(jail).createDirectory();
-
- Path jailLOInstallation(jail, LOOLWSD::loSubPath);
- jailLOInstallation.makeDirectory();
- File(jailLOInstallation).createDirectory();
-
- // Copy (link) LO installation and other necessary files into it from the template
-
- linkOrCopy(LOOLWSD::sysTemplate, jail);
- linkOrCopy(LOOLWSD::loTemplate, jailLOInstallation);
-
-#ifdef __linux
- // Create the urandom and random devices
- File(Path(jail, "/dev")).createDirectory();
- if (mknod((jail.toString() + "/dev/random").c_str(),
- S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH,
- makedev(1, 8)) != 0)
- {
- Application::instance().logger().error(Util::logPrefix() +
- "mknod(" + jail.toString() + "/dev/random) failed: " +
- strerror(errno));
-
- }
- if (mknod((jail.toString() + "/dev/urandom").c_str(),
- S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH,
- makedev(1, 9)) != 0)
- {
- Application::instance().logger().error(Util::logPrefix() +
- "mknod(" + jail.toString() + "/dev/urandom) failed: " +
- strerror(errno));
-
- }
-#endif
-
- _pendingPreSpawnedChildren.insert(childId);
-
- Process::Args args;
-
-#if ENABLE_DEBUG
- if (LOOLWSD::runningAsRoot)
- args.push_back(Application::instance().commandPath());
-#endif
-
- args.push_back("--child=" + std::to_string(childId));
- args.push_back("--port=" + std::to_string(LOOLWSD::portNumber));
- args.push_back("--jail=" + jail.toString());
- args.push_back("--losubpath=" + LOOLWSD::loSubPath);
-
- std::string executable;
-
-#if ENABLE_DEBUG
- if (LOOLWSD::runningAsRoot)
- {
- args.push_back("--uid=" + std::to_string(LOOLWSD::uid));
- executable = "/usr/bin/sudo";
- }
- else
-#endif
- {
- executable = Application::instance().commandPath();
- }
-
- Application::instance().logger().information(Util::logPrefix() + "Launching child: " + executable + " " + Poco::cat(std::string(" "), args.begin(), args.end()));
-
-#if ENABLE_DEBUG
- ProcessHandle child = Process::launch(executable, args);
-#else
- ProcessHandle child = Process::launch(Application::instance().commandPath(), args);
-#endif
+ return _availableChildSessions.size();
+}
- _childProcesses[child.id()] = childId;
+int MasterProcessSession::getPendingPreSpawnedChildren()
+{
+ return _pendingPreSpawnedChildren.size();
}
+
bool MasterProcessSession::invalidateTiles(const char *buffer, int length, StringTokenizer& tokens)
{
int part, tilePosX, tilePosY, tileWidth, tileHeight;
@@ -616,9 +477,6 @@ void MasterProcessSession::dispatchChild()
{
// Running out of pre-spawned children, so spawn one more
Application::instance().logger().information(Util::logPrefix() + "Running out of pre-spawned childred, adding one more");
- lock.unlock();
- preSpawn();
- lock.lock();
}
std::cout << Util::logPrefix() << "waiting for a child session to become available" << std::endl;
@@ -640,46 +498,29 @@ void MasterProcessSession::dispatchChild()
if (!aUri.empty() && aUri.getScheme() == "file")
{
- Path aSrcFile(aUri.getPath());
- Path aDstFile(Path(getJailPath(childSession->_childId), jailDocumentURL.substr(1)), aSrcFile.getFileName());
- Path aDstPath(getJailPath(childSession->_childId), jailDocumentURL.substr(1));
- Path aJailFile(jailDocumentURL, aSrcFile.getFileName());
-
+ // The process is jail rooted, so it requests loolMain process to transfer files.
try
{
- File(aDstPath).createDirectories();
+ Path aSrcFile(aUri.getPath());
+ Path aDstFile(Path(getJailPath(childSession->_childId), jailDocumentURL.substr(1)), aSrcFile.getFileName());
+ std::string sCopy(aSrcFile.toString() + " " + aDstFile.toString());
+ std::string str;
+
+ DialogSocket ds;
+ ds.connect(SocketAddress("127.0.0.1", LOOLWSD::FILE_PORT_NUMBER));
+ ds.sendMessage(sCopy);
+ ds.receiveMessage(str);
+
+ if (str != "OK")
+ Application::instance().logger().error( Util::logPrefix() +
+ "DataSocket copyTo(\"" + aSrcFile.toString() + "\",\"" + aDstFile.toString() + "\") failed: " + str);
}
catch (Exception& exc)
{
Application::instance().logger().error( Util::logPrefix() +
- "createDirectories(\"" + aDstPath.toString() + "\") failed: " + exc.displayText() );
-
+ "FileTransferHanlder failed: " + exc.displayText());
}
-#ifdef __linux
- Application::instance().logger().information(Util::logPrefix() + "Linking " + aSrcFile.toString() + " to " + aDstFile.toString());
- if (link(aSrcFile.toString().c_str(), aDstFile.toString().c_str()) == -1)
- {
- // Failed
- Application::instance().logger().error( Util::logPrefix() +
- "link(\"" + aSrcFile.toString() + "\",\"" + aDstFile.toString() + "\") failed: " + strerror(errno) );
- }
-#endif
-
- try
- {
- //fallback
- if (!File(aDstFile).exists())
- {
- Application::instance().logger().information(Util::logPrefix() + "Copying " + aSrcFile.toString() + " to " + aDstFile.toString());
- File(aSrcFile).copyTo(aDstFile.toString());
- }
- }
- catch (Exception& exc)
- {
- Application::instance().logger().error( Util::logPrefix() +
- "copyTo(\"" + aSrcFile.toString() + "\",\"" + aDstFile.toString() + "\") failed: " + exc.displayText());
- }
}
_peer = childSession;
@@ -687,9 +528,6 @@ void MasterProcessSession::dispatchChild()
std::string loadRequest = "load url=" + _docURL;
forwardToPeer(loadRequest.c_str(), loadRequest.size());
-
- // As we took one child process into use, spawn a new one
- preSpawn();
}
void MasterProcessSession::forwardToPeer(const char *buffer, int length)
@@ -919,6 +757,7 @@ bool ChildProcessSession::loadDocument(const char *buffer, int length, StringTok
sendTextFrame("error: cmd=load kind=failed");
return false;
}
+
_loKitDocument->pClass->initializeForRendering(_loKitDocument);
if (!getStatus(buffer, length))
diff --git a/loolwsd/LOOLSession.hpp b/loolwsd/LOOLSession.hpp
index 221de8b..a5381ca 100644
--- a/loolwsd/LOOLSession.hpp
+++ b/loolwsd/LOOLSession.hpp
@@ -102,10 +102,9 @@ public:
bool haveSeparateProcess();
static Poco::Path getJailPath(Poco::UInt64 childId);
-
- // Set up the chroot environment for one child process and start
- // it, in advance of it being actually needed
- static void preSpawn();
+ static void addPendingChildrem(Poco::UInt64 childId);
+ static int getAvailableChildSessions();
+ static int getPendingPreSpawnedChildren();
static std::map<Poco::Process::PID, Poco::UInt64> _childProcesses;
diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index 6856429..631e2b3 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -50,10 +50,15 @@ DEALINGS IN THE SOFTWARE.
#include <sys/wait.h>
#endif
+#include <ftw.h>
+#include <utime.h>
+
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <iostream>
+#include <sstream>
+#include <mutex>
#define LOK_USE_UNSTABLE_API
#include <LibreOfficeKit/LibreOfficeKitInit.h>
@@ -82,6 +87,12 @@ DEALINGS IN THE SOFTWARE.
#include <Poco/Util/OptionException.h>
#include <Poco/Util/OptionSet.h>
#include <Poco/Util/ServerApplication.h>
+#include <Poco/Mutex.h>
+#include <Poco/Net/DialogSocket.h>
+#include <Poco/Net/Net.h>
+#include <Poco/ThreadLocal.h>
+#include <Poco/NamedMutex.h>
+
#include "LOOLProtocol.hpp"
#include "LOOLSession.hpp"
@@ -120,6 +131,12 @@ using Poco::Util::MissingOptionException;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::ServerApplication;
+using Poco::Net::DialogSocket;
+using Poco::FastMutex;
+using Poco::Net::Socket;
+using Poco::ThreadLocal;
+using Poco::Random;
+using Poco::NamedMutex;
class QueueHandler: public Runnable
{
@@ -392,71 +409,92 @@ private:
HTTPServer& _srv;
};
-class Undertaker : public Runnable
+class FileTransferHandler : public Runnable
{
public:
- Undertaker()
+ FileTransferHandler() : _socket(ServerSocket(LOOLWSD::FILE_PORT_NUMBER))
{
}
void run() override
{
- bool someChildrenHaveDied = false;
+ Poco::Timespan span(250000);
- while (!someChildrenHaveDied || MasterProcessSession::_childProcesses.size() > 0)
+ while (true)
{
- int status;
- pid_t pid = waitpid(-1, &status, 0);
- if (pid < 0)
+ if (_socket.poll(span, Socket::SELECT_READ))
{
- if (errno == ECHILD)
+ DialogSocket ds = _socket.acceptConnection();
+
+ try
{
- if (!someChildrenHaveDied)
+ std::string command;
+ while (ds.receiveMessage(command))
{
- // We haven't spawned any children yet, or at least none has died yet. So
- // wait a bit and try again
- Thread::sleep(1000);
- continue;
- }
- else
- {
- // We have spawned children, and we think that we still have them running,
- // but we don't, huh? Something badly messed up, or just a timing glitch,
- // like we are at the moment in the process of spawning new children?
- // Sleep or return from the function (i.e. finish the Undertaker thread)?
- std::cout << Util::logPrefix() << "No child processes even if we think there should be some!?" << std::endl;
- return;
+ FastMutex::ScopedLock lock(_mutex);
+ ds.sendMessage(transferFile(command));
}
}
+ catch (Poco::Exception& exc)
+ {
+ std::cerr << "FileTransferHandler: " << exc.displayText() << std::endl;
+ }
}
+ }
+ }
+
+ std::string transferFile(std::string command)
+ {
+ StringTokenizer tokens(command, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+ if ( tokens.count() != 2 )
+ return "Souce and Destination is needed :" + command;
+
+ Path aSrcFile(tokens[0]);
+ Path aDstFile(tokens[1]);
+ Path aDstPath(aDstFile.parent());
+
+ try
+ {
+ File(aDstPath).createDirectories();
+ }
+ catch (Exception& exc)
+ {
+ return exc.displayText();
+ }
- if (WIFSIGNALED(status))
- Application::instance().logger().error(Util::logPrefix() + "Child " + std::to_string(pid) + " killed by signal " + Util::signalName(WTERMSIG(status)));
- else
- Application::instance().logger().information(Util::logPrefix() + "Child " + std::to_string(pid) + " died normally, status: " + std::to_string(WEXITSTATUS(status)));
+#ifdef __linux
+ Application::instance().logger().information(Util::logPrefix() + "Linking " + aSrcFile.toString() + " to " + aDstFile.toString());
+ if (link(aSrcFile.toString().c_str(), aDstFile.toString().c_str()) == -1)
+ {
+ // Failed
+ Application::instance().logger().error( Util::logPrefix() +
+ "link(\"" + aSrcFile.toString() + "\",\"" + aDstFile.toString() + "\") failed: " + strerror(errno) );
+ }
+#endif
- if (MasterProcessSession::_childProcesses.find(pid) == MasterProcessSession::_childProcesses.end())
- std::cout << Util::logPrefix() << "(Not one of our known child processes)" << std::endl;
- else
+ try
+ {
+ //fallback
+ if (!File(aDstFile).exists())
{
- File jailDir(MasterProcessSession::getJailPath(MasterProcessSession::_childProcesses[pid]));
- MasterProcessSession::_childProcesses.erase(pid);
- someChildrenHaveDied = true;
- if (!jailDir.exists() || !jailDir.isDirectory())
- {
- Application::instance().logger().error(Util::logPrefix() + "Jail '" + jailDir.path() + "' does not exist or is not a directory");
- }
- else
- {
- std::cout << Util::logPrefix() << "Removing jail tree " << jailDir.path() << std::endl;
- jailDir.remove(true);
- }
+ Application::instance().logger().information(Util::logPrefix() + "Copying " + aSrcFile.toString() + " to " + aDstFile.toString());
+ File(aSrcFile).copyTo(aDstFile.toString());
}
}
- std::cout << Util::logPrefix() << "All child processes have died (I hope)" << std::endl;
+ catch (Exception& exc)
+ {
+ return exc.displayText();
+ }
+
+ return "OK";
}
+
+private:
+ Poco::Net::ServerSocket _socket;
+ mutable Poco::FastMutex _mutex;
};
+
int LOOLWSD::portNumber = DEFAULT_CLIENT_PORT_NUMBER;
std::string LOOLWSD::cache = LOOLWSD_CACHEDIR;
std::string LOOLWSD::sysTemplate;
@@ -464,6 +502,10 @@ std::string LOOLWSD::loTemplate;
std::string LOOLWSD::childRoot;
std::string LOOLWSD::loSubPath = "lo";
std::string LOOLWSD::jail;
+std::mutex LOOLWSD::_rngMutex;
+Random LOOLWSD::_rng;
+static NamedMutex namedMutexLOOL("loolwsd");
+
int LOOLWSD::_numPreSpawnedChildren = 10;
#if ENABLE_DEBUG
bool LOOLWSD::runningAsRoot = false;
@@ -603,6 +645,91 @@ void LOOLWSD::displayHelp()
namespace
{
+ ThreadLocal<std::string> sourceForLinkOrCopy;
+ ThreadLocal<Path> destinationForLinkOrCopy;
+
+ int linkOrCopyFunction(const char *fpath,
+ const struct stat *sb,
+ int typeflag,
+ struct FTW *ftwbuf)
+ {
+ if (strcmp(fpath, sourceForLinkOrCopy->c_str()) == 0)
+ return 0;
+
+ assert(fpath[strlen(sourceForLinkOrCopy->c_str())] == '/');
+ const char *relativeOldPath = fpath + strlen(sourceForLinkOrCopy->c_str()) + 1;
+
+#ifdef __APPLE__
+ if (strcmp(relativeOldPath, "PkgInfo") == 0)
+ return 0;
+#endif
+
+ Path newPath(*destinationForLinkOrCopy, Path(relativeOldPath));
+
+ switch (typeflag)
+ {
+ case FTW_F:
+ File(newPath.parent()).createDirectories();
+ if (link(fpath, newPath.toString().c_str()) == -1)
+ {
+ Application::instance().logger().error(Util::logPrefix() +
+ "link(\"" + fpath + "\",\"" + newPath.toString() + "\") failed: " +
+ strerror(errno));
+ exit(1);
+ }
+ break;
+ case FTW_DP:
+ {
+ struct stat st;
+ if (stat(fpath, &st) == -1)
+ {
+ Application::instance().logger().error(Util::logPrefix() +
+ "stat(\"" + fpath + "\") failed: " +
+ strerror(errno));
+ return 1;
+ }
+ File(newPath).createDirectories();
+ struct utimbuf ut;
+ ut.actime = st.st_atime;
+ ut.modtime = st.st_mtime;
+ if (utime(newPath.toString().c_str(), &ut) == -1)
+ {
+ Application::instance().logger().error(Util::logPrefix() +
+ "utime(\"" + newPath.toString() + "\", &ut) failed: " +
+ strerror(errno));
+ return 1;
+ }
+ }
+ break;
+ case FTW_DNR:
+ Application::instance().logger().error(Util::logPrefix() +
+ "Cannot read directory '" + fpath + "'");
+ return 1;
+ case FTW_NS:
+ Application::instance().logger().error(Util::logPrefix() +
+ "nftw: stat failed for '" + fpath + "'");
+ return 1;
+ case FTW_SLN:
+ Application::instance().logger().information(Util::logPrefix() +
+ "nftw: symlink to nonexistent file: '" + fpath + "', ignored");
+ break;
+ default:
+ assert(false);
+ }
+ return 0;
+ }
+
+ void linkOrCopy(const std::string& source, const Path& destination)
+ {
+ *sourceForLinkOrCopy = source;
+ if (sourceForLinkOrCopy->back() == '/')
+ sourceForLinkOrCopy->pop_back();
+ *destinationForLinkOrCopy = destination;
+ if (nftw(source.c_str(), linkOrCopyFunction, 10, FTW_DEPTH) == -1)
+ Application::instance().logger().error(Util::logPrefix() +
+ "linkOrCopy: nftw() failed for '" + source + "'");
+ }
+
void dropCapability(
#ifdef __linux
cap_value_t capability
@@ -677,55 +804,14 @@ namespace
}
}
-int LOOLWSD::childMain()
+// Writer, Impress or Calc
+void LOOLWSD::componentMain()
{
- std::cout << Util::logPrefix() << "Child here! id=" << _childId << std::endl;
-
-#ifdef __linux
- dropCapability(CAP_FOWNER);
- dropCapability(CAP_MKNOD);
-#endif
- // We use the same option set for both parent and child loolwsd,
- // so must check options required in the child (but not in the
- // parent) separately now. And also for options that are
- // meaningless to the child.
- if (jail == "")
- throw MissingOptionException("systemplate");
-
- if (sysTemplate != "")
- throw IncompatibleOptionsException("systemplate");
- if (loTemplate != "")
- throw IncompatibleOptionsException("lotemplate");
- if (childRoot != "")
- throw IncompatibleOptionsException("childroot");
-
- if (chroot(jail.c_str()) == -1)
- {
- logger().error("chroot(\"" + jail + "\") failed: " + strerror(errno));
- exit(1);
- }
-
-#ifdef __linux
- dropCapability(CAP_SYS_CHROOT);
-#else
- dropCapability();
-#endif
-
- if (chdir("/") == -1)
- {
- logger().error(std::string("chdir(\"/\") in jail failed: ") + strerror(errno));
- exit(1);
- }
-
- if (std::getenv("SLEEPFORDEBUGGER"))
- {
- std::cout << "Sleeping " << std::getenv("SLEEPFORDEBUGGER") << " seconds, " <<
- "attach process " << Process::id() << " in debugger now." << std::endl;
- Thread::sleep(std::stoul(std::getenv("SLEEPFORDEBUGGER")) * 1000);
- }
-
try
{
+ // initialisation
+ //_childId = Process::id();
+
#ifdef __APPLE__
LibreOfficeKit *loKit(lok_init_2(("/" + loSubPath + "/Frameworks").c_str(), "file:///user"));
#else
@@ -735,9 +821,13 @@ int LOOLWSD::childMain()
if (!loKit)
{
logger().fatal(Util::logPrefix() + "LibreOfficeKit initialisation failed");
- return Application::EXIT_UNAVAILABLE;
+ exit(Application::EXIT_UNAVAILABLE);
}
+ // wait until desktop sockets setup
+ namedMutexLOOL.lock();
+ namedMutexLOOL.unlock();
+
// Open websocket connection between the child process and the
// parent. The parent forwards us requests that it can't handle.
@@ -803,14 +893,92 @@ int LOOLWSD::childMain()
logger().error(Util::logPrefix() + "Exception: " + exc.what());
}
- // Safest to just bluntly exit
- _Exit(Application::EXIT_OK);
+ exit(Application::EXIT_OK);
}
-int LOOLWSD::main(const std::vector<std::string>& args)
+int LOOLWSD::createComponent()
{
- if (childMode())
- return childMain();
+ int pid;
+
+ if ((pid = fork()) == -1)
+ {
+ std::cout << "Fork failed." << std::endl;
+ return Application::EXIT_UNAVAILABLE;
+ }
+
+ if (!pid)
+ {
+ componentMain();
+ }
+
+ MasterProcessSession::addPendingChildrem(pid);
+ MasterProcessSession::_childProcesses[pid] = pid;
+
+ return Application::EXIT_OK;
+}
+
+void LOOLWSD::startupComponent(int nComponents)
+{
+ for (int nCntr = nComponents; nCntr; nCntr--)
+ {
+ if (createComponent() < 0)
+ break;
+ }
+}
+
+void LOOLWSD::desktopMain()
+{
+ // Initialization
+ std::unique_lock<std::mutex> rngLock(_rngMutex);
+ _childId = (((Poco::UInt64)_rng.next()) << 32) | _rng.next() | 1;
+ rngLock.unlock();
+
+ Path jail = Path::forDirectory(LOOLWSD::childRoot + Path::separator() + std::to_string(_childId));
+ File(jail).createDirectory();
+
+ Path jailLOInstallation(jail, LOOLWSD::loSubPath);
+ jailLOInstallation.makeDirectory();
+ File(jailLOInstallation).createDirectory();
+
+ // Copy (link) LO installation and other necessary files into it from the template
+
+ linkOrCopy(LOOLWSD::sysTemplate, jail);
+ linkOrCopy(LOOLWSD::loTemplate, jailLOInstallation);
+
+#ifdef __linux
+ // Create the urandom and random devices
+ File(Path(jail, "/dev")).createDirectory();
+ if (mknod((jail.toString() + "/dev/random").c_str(),
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH,
+ makedev(1, 8)) != 0)
+ {
+ Application::instance().logger().error(Util::logPrefix() +
+ "mknod(" + jail.toString() + "/dev/random) failed: " +
+ strerror(errno));
+
+ }
+ if (mknod((jail.toString() + "/dev/urandom").c_str(),
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH,
+ makedev(1, 9)) != 0)
+ {
+ Application::instance().logger().error(Util::logPrefix() +
+ "mknod(" + jail.toString() + "/dev/urandom) failed: " +
+ strerror(errno));
+ }
+#endif
+
+ Application::instance().logger().information("desktopMain -> chroot(\"" + jail.toString() + "\")");
+ if (chroot(jail.toString().c_str()) == -1)
+ {
+ logger().error("chroot(\"" + jail.toString() + "\") failed: " + strerror(errno));
+ exit(Application::EXIT_UNAVAILABLE);
+ }
+
+ if (chdir("/") == -1)
+ {
+ logger().error(std::string("chdir(\"/\") in jail failed: ") + strerror(errno));
+ exit(Application::EXIT_UNAVAILABLE);
+ }
#ifdef __linux
dropCapability(CAP_SYS_CHROOT);
@@ -818,36 +986,30 @@ int LOOLWSD::main(const std::vector<std::string>& args)
dropCapability();
#endif
- if (access(cache.c_str(), R_OK | W_OK | X_OK) != 0)
+ if (std::getenv("SLEEPFORDEBUGGER"))
{
- std::cout << "Unable to access " << cache <<
- ", please make sure it exists, and has write permission for this user." << std::endl;
- return Application::EXIT_UNAVAILABLE;
+ std::cout << "Sleeping " << std::getenv("SLEEPFORDEBUGGER") << " seconds, " <<
+ "attach process " << Process::id() << " in debugger now." << std::endl;
+ Thread::sleep(std::stoul(std::getenv("SLEEPFORDEBUGGER")) * 1000);
}
- // We use the same option set for both parent and child loolwsd,
- // so must check options required in the parent (but not in the
- // child) separately now. Also check for options that are
- // meaningless for the parent.
- if (sysTemplate == "")
- throw MissingOptionException("systemplate");
- if (loTemplate == "")
- throw MissingOptionException("lotemplate");
- if (childRoot == "")
- throw MissingOptionException("childroot");
+ // TODO loKit SolarMutex is locked, it ends deadlock
+/*
+#ifdef __APPLE__
+ LibreOfficeKit *loKit(lok_init_2(("/" + loSubPath + "/Frameworks").c_str(), "file:///user"));
+#else
+ LibreOfficeKit *loKit(lok_init_2(("/" + loSubPath + "/program").c_str(), "file:///user"));
+#endif
- if (_childId != 0)
- throw IncompatibleOptionsException("child");
- if (jail != "")
- throw IncompatibleOptionsException("jail");
- if (portNumber == MASTER_PORT_NUMBER)
- throw IncompatibleOptionsException("port");
+ if (!loKit)
+ {
+ logger().fatal(Util::logPrefix() + "LibreOfficeKit initialisation failed");
+ exit(Application::EXIT_UNAVAILABLE);
+ }*/
+ namedMutexLOOL.lock();
- // Set up a thread to reap child processes and clean up after them
- Undertaker undertaker;
- Thread undertakerThread;
- undertakerThread.start(undertaker);
+ startupComponent(_numPreSpawnedChildren);
// Start a server listening on the port for clients
ServerSocket svs(portNumber, _numPreSpawnedChildren*10);
@@ -864,26 +1026,97 @@ int LOOLWSD::main(const std::vector<std::string>& args)
srv2.start();
- if (_doTest)
- _numPreSpawnedChildren = 1;
+ namedMutexLOOL.unlock();
- for (int i = 0; i < _numPreSpawnedChildren; i++)
- MasterProcessSession::preSpawn();
+ while (MasterProcessSession::_childProcesses.size() > 0)
+ {
+ int status;
+ pid_t pid = waitpid(-1, &status, WUNTRACED | WNOHANG);
+ if (pid < 0)
+ {
+ if (errno == ECHILD)
+ {
+ // We have spawned children, and we think that we still have them running,
+ // but we don't, huh? Something badly messed up, or just a timing glitch,
+ // like we are at the moment in the process of spawning new children?
+ // Sleep or return from the function (i.e. finish the Undertaker thread)?
+ std::cout << Util::logPrefix() << "No child processes even if we think there should be some!?" << std::endl;
+ }
+ }
- TestInput input(*this, svs, srv);
- Thread inputThread;
- if (_doTest)
+ if ((WIFEXITED(status) || WIFSIGNALED(status) || WTERMSIG(status) ) &&
+ MasterProcessSession::_childProcesses.find(pid) != MasterProcessSession::_childProcesses.end())
+ std::cout << Util::logPrefix() << "One of our known child processes died" << std::endl;
+
+ if (WSTOPSIG(status) && MasterProcessSession::getAvailableChildSessions() == 0 && MasterProcessSession::getPendingPreSpawnedChildren() == 0 )
+ {
+ std::cout << Util::logPrefix() << "No availabe child session, fork new one" << std::endl;
+ if (createComponent() < 0 )
+ break;
+ }
+ }
+
+ // Terminate child processes
+ for (auto i : MasterProcessSession::_childProcesses)
{
- inputThread.start(input);
+ logger().information(Util::logPrefix() + "Requesting child process " + std::to_string(i.first) + " to terminate");
+ Process::requestTermination(i.first);
}
- waitForTerminationRequest();
+ exit(Application::EXIT_OK);
+}
- // Doing this causes a crash. So just let the process proceed and exit.
- // srv.stop();
- if (_doTest)
- inputThread.join();
+int LOOLWSD::createDesktop()
+{
+ int pid;
+
+ if ((pid = fork()) == -1)
+ {
+ std::cout << "createDesktop fork failed." << std::endl;
+ return Application::EXIT_UNAVAILABLE;
+ }
+
+ if (!pid)
+ {
+ desktopMain();
+ }
+
+ MasterProcessSession::_childProcesses[pid] = pid;
+
+ return Application::EXIT_OK;
+}
+
+void LOOLWSD::startupDesktop(int nDesktops)
+{
+ for (int nCntr = nDesktops; nCntr; nCntr--)
+ {
+ if (createDesktop() < 0)
+ break;
+ }
+}
+
+
+void LOOLWSD::loolMain()
+{
+ std::unique_lock<std::mutex> rngLock(_rngMutex);
+ _childId = (((Poco::UInt64)_rng.next()) << 32) | _rng.next() | 1;
+ rngLock.unlock();
+
+ startupDesktop(1);
+
+#ifdef __linux
+ dropCapability(CAP_SYS_CHROOT);
+#else
+ dropCapability();
+#endif
+
+ Thread threadFile;
+ FileTransferHandler svrFile;
+ threadFile.start(svrFile);
+
+ int status;
+ waitpid(-1, &status, 0);
// Terminate child processes
for (auto i : MasterProcessSession::_childProcesses)
@@ -891,8 +1124,39 @@ int LOOLWSD::main(const std::vector<std::string>& args)
logger().information(Util::logPrefix() + "Requesting child process " + std::to_string(i.first) + " to terminate");
Process::requestTermination(i.first);
}
+}
+
+int LOOLWSD::main(const std::vector<std::string>& args)
+{
+ if (access(cache.c_str(), R_OK | W_OK | X_OK) != 0)
+ {
+ std::cout << "Unable to access " << cache <<
+ ", please make sure it exists, and has write permission for this user." << std::endl;
+ return Application::EXIT_UNAVAILABLE;
+ }
+
+ // We use the same option set for both parent and child loolwsd,
+ // so must check options required in the parent (but not in the
+ // child) separately now. Also check for options that are
+ // meaningless for the parent.
+ if (sysTemplate == "")
+ throw MissingOptionException("systemplate");
+ if (loTemplate == "")
+ throw MissingOptionException("lotemplate");
+ if (childRoot == "")
+ throw MissingOptionException("childroot");
+
+ if (_childId != 0)
+ throw IncompatibleOptionsException("child");
+ if (jail != "")
+ throw IncompatibleOptionsException("jail");
+ if (portNumber == MASTER_PORT_NUMBER)
+ throw IncompatibleOptionsException("port");
+
+ if (_doTest)
+ _numPreSpawnedChildren = 1;
- undertakerThread.join();
+ loolMain();
return Application::EXIT_OK;
}
diff --git a/loolwsd/LOOLWSD.hpp b/loolwsd/LOOLWSD.hpp
index 56a8939..bccb98f 100644
--- a/loolwsd/LOOLWSD.hpp
+++ b/loolwsd/LOOLWSD.hpp
@@ -13,8 +13,11 @@
#include "config.h"
#include <string>
+#include <mutex>
#include <Poco/Util/OptionSet.h>
+#include <Poco/Random.h>
+#include <Poco/Path.h>
#include <Poco/Util/ServerApplication.h>
class LOOLWSD: public Poco::Util::ServerApplication
@@ -35,6 +38,7 @@ public:
static const int DEFAULT_CLIENT_PORT_NUMBER = 9980;
static const int MASTER_PORT_NUMBER = 9981;
+ static const int FILE_PORT_NUMBER = 9979;
static const std::string CHILD_URI;
protected:
@@ -46,12 +50,20 @@ protected:
private:
void displayHelp();
- int childMain();
bool childMode() const;
+ void componentMain();
+ void desktopMain();
+ void loolMain();
+ void startupComponent(int nComponents);
+ void startupDesktop(int nDesktop);
+ int createComponent();
+ int createDesktop();
bool _doTest;
Poco::UInt64 _childId;
static int _numPreSpawnedChildren;
+ static std::mutex _rngMutex;
+ static Poco::Random _rng;
#if ENABLE_DEBUG
public:
More information about the Libreoffice-commits
mailing list