[Libreoffice-commits] online.git: Branch 'private/hcvcastro/forking' - loolwsd/ChildProcessSession.cpp loolwsd/ChildProcessSession.hpp loolwsd/LOOLBroker.cpp loolwsd/LOOLKit.cpp loolwsd/LOOLSession.cpp loolwsd/LOOLSession.hpp loolwsd/LOOLWSD.cpp loolwsd/LOOLWSD.hpp loolwsd/loolwsd-systemplate-setup loolwsd/Makefile.am loolwsd/MasterProcessSession.cpp loolwsd/MasterProcessSession.hpp

Henry Castro hcastro at collabora.com
Thu Jul 23 06:29:14 PDT 2015


 loolwsd/ChildProcessSession.cpp   |  560 +++++++++++++++++++++
 loolwsd/ChildProcessSession.hpp   |   57 ++
 loolwsd/LOOLBroker.cpp            |  451 +++++++++++++++++
 loolwsd/LOOLKit.cpp               |  164 ++++++
 loolwsd/LOOLSession.cpp           |  975 --------------------------------------
 loolwsd/LOOLSession.hpp           |  102 ---
 loolwsd/LOOLWSD.cpp               |  613 +++--------------------
 loolwsd/LOOLWSD.hpp               |    9 
 loolwsd/Makefile.am               |   21 
 loolwsd/MasterProcessSession.cpp  |  571 ++++++++++++++++++++++
 loolwsd/MasterProcessSession.hpp  |   73 ++
 loolwsd/loolwsd-systemplate-setup |   42 +
 12 files changed, 2030 insertions(+), 1608 deletions(-)

New commits:
commit b9a04ecac1f618e770cab227db2996ad5e1135a6
Author: Henry Castro <hcastro at collabora.com>
Date:   Thu Jul 23 09:23:24 2015 -0400

    loolwsd: Initial setup
    
    Separate classes, MasterProcessSession, ChildProcessSession
    config loolwsd-systemplate-setup, adjust code, create
    process LOOBroker and LOOLKit

diff --git a/loolwsd/ChildProcessSession.cpp b/loolwsd/ChildProcessSession.cpp
new file mode 100644
index 0000000..49e6a43
--- /dev/null
+++ b/loolwsd/ChildProcessSession.cpp
@@ -0,0 +1,560 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <ftw.h>
+#include <utime.h>
+
+#include <cassert>
+#include <condition_variable>
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <iterator>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <set>
+
+/*#define LOK_USE_UNSTABLE_API
+#include <LibreOfficeKit/LibreOfficeKit.h>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>*/
+
+#include <Poco/Net/WebSocket.h>
+#include <Poco/StringTokenizer.h>
+#include <Poco/Util/Application.h>
+#include <Poco/URI.h>
+#include <Poco/Path.h>
+
+#include "ChildProcessSession.hpp"
+#include "Util.hpp"
+#include "LOOLProtocol.hpp"
+#include "LOKitHelper.hpp"
+
+using namespace LOOLProtocol;
+using Poco::Net::WebSocket;
+using Poco::StringTokenizer;
+using Poco::Util::Application;
+using Poco::URI;
+using Poco::Path;
+
+/*
+
+#include <Poco/Exception.h>
+#include <Poco/File.h>
+#include <Poco/Net/HTTPStreamFactory.h>
+#include <Poco/Process.h>
+#include <Poco/Random.h>
+#include <Poco/StreamCopier.h>
+#include <Poco/String.h>
+#include <Poco/ThreadLocal.h>
+#include <Poco/URIStreamOpener.h>
+#include <Poco/Exception.h>
+#include <Poco/Net/NetException.h>
+#include <Poco/Net/DialogSocket.h>
+#include <Poco/Net/SocketAddress.h>
+
+#include "LOOLSession.hpp"
+#include "LOOLWSD.hpp"
+#include "TileCache.hpp"
+
+
+using Poco::File;
+using Poco::IOException;
+using Poco::Net::HTTPStreamFactory;
+using Poco::Process;
+using Poco::ProcessHandle;
+using Poco::Random;
+using Poco::StreamCopier;
+using Poco::Thread;
+using Poco::ThreadLocal;
+using Poco::UInt64;
+using Poco::URIStreamOpener;
+using Poco::Util::Application;
+using Poco::Exception;
+using Poco::Net::DialogSocket;
+using Poco::Net::SocketAddress;
+using Poco::Net::WebSocketException;*/
+
+
+ChildProcessSession::ChildProcessSession(std::shared_ptr<WebSocket> ws, LibreOfficeKit *loKit) :
+    LOOLSession(ws, Kind::ToMaster),
+    _loKitDocument(NULL),
+    _loKit(loKit),
+    _clientPart(0)
+{
+    std::cout << Util::logPrefix() << "ChildProcessSession ctor this=" << this << " ws=" << _ws.get() << std::endl;
+}
+
+ChildProcessSession::~ChildProcessSession()
+{
+    std::cout << Util::logPrefix() << "ChildProcessSession dtor this=" << this << std::endl;
+    if (LIBREOFFICEKIT_HAS(_loKit, registerCallback))
+        _loKit->pClass->registerCallback(_loKit, 0, 0);
+    Util::shutdownWebSocket(*_ws);
+}
+
+bool ChildProcessSession::handleInput(const char *buffer, int length)
+{
+    std::string firstLine = getFirstLine(buffer, length);
+    StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+
+    Application::instance().logger().information(Util::logPrefix() + _kindString + ",Input," + getAbbreviatedMessage(buffer, length));
+
+    if (tokens[0] == "load")
+    {
+        if (_docURL != "")
+        {
+            sendTextFrame("error: cmd=load kind=docalreadyloaded");
+            return false;
+        }
+        return loadDocument(buffer, length, tokens);
+    }
+    else if (_docURL == "")
+    {
+        sendTextFrame("error: cmd=" + tokens[0] + " kind=nodocloaded");
+        return false;
+    }
+    else if (tokens[0] == "setclientpart")
+    {
+        return setClientPart(buffer, length, tokens);
+    }
+    else if (tokens[0] == "status")
+    {
+        return getStatus(buffer, length);
+    }
+    else if (tokens[0] == "tile")
+    {
+        sendTile(buffer, length, tokens);
+    }
+    else
+    {
+        // All other commands are such that they always require a LibreOfficeKitDocument session,
+        // i.e. need to be handled in a child process.
+
+        assert(tokens[0] == "gettextselection" ||
+               tokens[0] == "key" ||
+               tokens[0] == "mouse" ||
+               tokens[0] == "uno" ||
+               tokens[0] == "selecttext" ||
+               tokens[0] == "selectgraphic" ||
+               tokens[0] == "resetselection" ||
+               tokens[0] == "saveas");
+
+        if (_loKitDocument->pClass->getPart(_loKitDocument) != _clientPart)
+        {
+            _loKitDocument->pClass->setPart(_loKitDocument, _clientPart);
+        }
+        if (tokens[0] == "gettextselection")
+        {
+            return getTextSelection(buffer, length, tokens);
+        }
+        else if (tokens[0] == "key")
+        {
+            return keyEvent(buffer, length, tokens);
+        }
+        else if (tokens[0] == "mouse")
+        {
+            return mouseEvent(buffer, length, tokens);
+        }
+        else if (tokens[0] == "uno")
+        {
+            return unoCommand(buffer, length, tokens);
+        }
+        else if (tokens[0] == "selecttext")
+        {
+            return selectText(buffer, length, tokens);
+        }
+        else if (tokens[0] == "selectgraphic")
+        {
+            return selectGraphic(buffer, length, tokens);
+        }
+        else if (tokens[0] == "resetselection")
+        {
+            return resetSelection(buffer, length, tokens);
+        }
+        else if (tokens[0] == "saveas")
+        {
+            return saveAs(buffer, length, tokens);
+        }
+        else
+        {
+            assert(false);
+        }
+    }
+    return true;
+}
+
+extern "C"
+{
+    static void myCallback(int nType, const char* pPayload, void* pData)
+    {
+        ChildProcessSession *srv = reinterpret_cast<ChildProcessSession *>(pData);
+
+        switch ((LibreOfficeKitCallbackType) nType)
+        {
+        case LOK_CALLBACK_INVALIDATE_TILES:
+            {
+                int curPart = srv->_loKitDocument->pClass->getPart(srv->_loKitDocument);
+                srv->sendTextFrame("curpart: part=" + std::to_string(curPart));
+                StringTokenizer tokens(std::string(pPayload), " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+                if (tokens.count() == 4)
+                {
+                    int x(std::stoi(tokens[0]));
+                    int y(std::stoi(tokens[1]));
+                    int width(std::stoi(tokens[2]));
+                    int height(std::stoi(tokens[3]));
+                    srv->sendTextFrame("invalidatetiles:"
+                                       " part=" + std::to_string(curPart) +
+                                       " x=" + std::to_string(x) +
+                                       " y=" + std::to_string(y) +
+                                       " width=" + std::to_string(width) +
+                                       " height=" + std::to_string(height));
+                }
+                else {
+                    srv->sendTextFrame("invalidatetiles: " + std::string(pPayload));
+                }
+            }
+            break;
+        case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+            srv->sendTextFrame("invalidatecursor: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_TEXT_SELECTION:
+            srv->sendTextFrame("textselection: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_TEXT_SELECTION_START:
+            srv->sendTextFrame("textselectionstart: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_TEXT_SELECTION_END:
+            srv->sendTextFrame("textselectionend: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_CURSOR_VISIBLE:
+            srv->sendTextFrame("cursorvisible: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_GRAPHIC_SELECTION:
+            srv->sendTextFrame("graphicselection: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_HYPERLINK_CLICKED:
+            srv->sendTextFrame("hyperlinkclicked: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_STATE_CHANGED:
+            srv->sendTextFrame("statechanged: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_STATUS_INDICATOR_START:
+            srv->sendTextFrame("statusindicatorstart:");
+            break;
+        case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
+            srv->sendTextFrame("statusindicatorsetvalue: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_STATUS_INDICATOR_FINISH:
+            srv->sendTextFrame("statusindicatorfinish:");
+            break;
+        case LOK_CALLBACK_SEARCH_NOT_FOUND:
+            srv->sendTextFrame("searchnotfound: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
+            srv->getStatus("", 0);
+            break;
+        case LOK_CALLBACK_SET_PART:
+            srv->sendTextFrame("setpart: " + std::string(pPayload));
+            break;
+        }
+    }
+}
+
+bool ChildProcessSession::loadDocument(const char *buffer, int length, StringTokenizer& tokens)
+{
+    if (tokens.count() != 2)
+    {
+        sendTextFrame("error: cmd=load kind=syntax");
+        return false;
+    }
+
+    if (tokens[1].find("url=") == 0)
+        _docURL = tokens[1].substr(strlen("url="));
+    else
+        _docURL = tokens[1];
+
+    URI aUri;
+    try
+    {
+        aUri = URI(_docURL);
+    }
+    catch(Poco::SyntaxException&)
+    {
+        sendTextFrame("error: cmd=load kind=URI invalid syntax");
+        return false;
+    }
+
+    if (aUri.empty())
+    {
+        sendTextFrame("error: cmd=load kind=URI empty");
+        return false;
+    }
+
+    // The URL in the request is the original one, not visible in the chroot jail.
+    // The child process uses the fixed name jailDocumentURL.
+
+    if (LIBREOFFICEKIT_HAS(_loKit, registerCallback))
+        _loKit->pClass->registerCallback(_loKit, myCallback, this);
+
+    if (aUri.isRelative() || aUri.getScheme() == "file")
+        aUri = URI( URI("file://"), Path(jailDocumentURL, Path(aUri.getPath()).getFileName()).toString() );
+
+    if ((_loKitDocument = _loKit->pClass->documentLoad(_loKit, aUri.toString().c_str())) == NULL)
+    {
+        sendTextFrame("error: cmd=load kind=failed");
+        return false;
+    }
+
+    _loKitDocument->pClass->initializeForRendering(_loKitDocument);
+
+    if (!getStatus(buffer, length))
+        return false;
+    _loKitDocument->pClass->registerCallback(_loKitDocument, myCallback, this);
+
+    return true;
+}
+
+bool ChildProcessSession::getStatus(const char *buffer, int length)
+{
+    std::string status = "status: " + LOKitHelper::documentStatus(_loKitDocument);
+
+    sendTextFrame(status);
+
+    return true;
+}
+
+void ChildProcessSession::sendTile(const char *buffer, int length, StringTokenizer& tokens)
+{
+    int part, width, height, tilePosX, tilePosY, tileWidth, tileHeight;
+
+    if (tokens.count() < 8 ||
+        !getTokenInteger(tokens[1], "part", part) ||
+        !getTokenInteger(tokens[2], "width", width) ||
+        !getTokenInteger(tokens[3], "height", height) ||
+        !getTokenInteger(tokens[4], "tileposx", tilePosX) ||
+        !getTokenInteger(tokens[5], "tileposy", tilePosY) ||
+        !getTokenInteger(tokens[6], "tilewidth", tileWidth) ||
+        !getTokenInteger(tokens[7], "tileheight", tileHeight))
+    {
+        sendTextFrame("error: cmd=tile kind=syntax");
+        return;
+    }
+
+    if (part < 0 ||
+        width <= 0 ||
+        height <= 0 ||
+        tilePosX < 0 ||
+        tilePosY < 0 ||
+        tileWidth <= 0 ||
+        tileHeight <= 0)
+    {
+        sendTextFrame("error: cmd=tile kind=invalid");
+        return;
+    }
+
+    std::string response = "tile: " + Poco::cat(std::string(" "), tokens.begin() + 1, tokens.end()) + "\n";
+
+    std::vector<char> output;
+    output.reserve(4 * width * height);
+    output.resize(response.size());
+    std::memcpy(output.data(), response.data(), response.size());
+
+    unsigned char *pixmap = new unsigned char[4 * width * height];
+    _loKitDocument->pClass->setPart(_loKitDocument, part);
+    _loKitDocument->pClass->paintTile(_loKitDocument, pixmap, width, height, tilePosX, tilePosY, tileWidth, tileHeight);
+
+    if (!Util::encodePNGAndAppendToBuffer(pixmap, width, height, output))
+    {
+        sendTextFrame("error: cmd=tile kind=failure");
+        return;
+    }
+
+    delete[] pixmap;
+
+    sendBinaryFrame(output.data(), output.size());
+}
+
+bool ChildProcessSession::getTextSelection(const char *buffer, int length, StringTokenizer& tokens)
+{
+    std::string mimeType;
+
+    if (tokens.count() != 2 ||
+        !getTokenString(tokens[1], "mimetype", mimeType))
+    {
+        sendTextFrame("error: cmd=gettextselection kind=syntax");
+        return false;
+    }
+
+    char *textSelection = _loKitDocument->pClass->getTextSelection(_loKitDocument, mimeType.c_str(), NULL);
+
+    sendTextFrame("textselectioncontent: " + std::string(textSelection));
+    return true;
+}
+
+bool ChildProcessSession::keyEvent(const char *buffer, int length, StringTokenizer& tokens)
+{
+    int type, charcode, keycode;
+
+    if (tokens.count() != 4 ||
+        !getTokenKeyword(tokens[1], "type",
+                         {{"input", LOK_KEYEVENT_KEYINPUT}, {"up", LOK_KEYEVENT_KEYUP}},
+                         type) ||
+        !getTokenInteger(tokens[2], "char", charcode) ||
+        !getTokenInteger(tokens[3], "key", keycode))
+    {
+        sendTextFrame("error: cmd=key kind=syntax");
+        return false;
+    }
+
+    _loKitDocument->pClass->postKeyEvent(_loKitDocument, type, charcode, keycode);
+
+    return true;
+}
+
+bool ChildProcessSession::mouseEvent(const char *buffer, int length, StringTokenizer& tokens)
+{
+    int type, x, y, count;
+
+    if (tokens.count() != 5 ||
+        !getTokenKeyword(tokens[1], "type",
+                         {{"buttondown", LOK_MOUSEEVENT_MOUSEBUTTONDOWN},
+                          {"buttonup", LOK_MOUSEEVENT_MOUSEBUTTONUP},
+                          {"move", LOK_MOUSEEVENT_MOUSEMOVE}},
+                         type) ||
+        !getTokenInteger(tokens[2], "x", x) ||
+        !getTokenInteger(tokens[3], "y", y) ||
+        !getTokenInteger(tokens[4], "count", count))
+    {
+        sendTextFrame("error: cmd=mouse kind=syntax");
+        return false;
+    }
+
+    _loKitDocument->pClass->postMouseEvent(_loKitDocument, type, x, y, count);
+
+    return true;
+}
+
+bool ChildProcessSession::unoCommand(const char *buffer, int length, StringTokenizer& tokens)
+{
+    if (tokens.count() == 1)
+    {
+        sendTextFrame("error: cmd=uno kind=syntax");
+        return false;
+    }
+
+    if (tokens.count() == 2)
+    {
+        _loKitDocument->pClass->postUnoCommand(_loKitDocument, tokens[1].c_str(), 0);
+    }
+    else
+    {
+        _loKitDocument->pClass->postUnoCommand(_loKitDocument, tokens[1].c_str(), Poco::cat(std::string(" "), tokens.begin() + 2, tokens.end()).c_str());
+    }
+
+    return true;
+}
+
+bool ChildProcessSession::selectText(const char *buffer, int length, StringTokenizer& tokens)
+{
+    int type, x, y;
+
+    if (tokens.count() != 4 ||
+        !getTokenKeyword(tokens[1], "type",
+                         {{"start", LOK_SETTEXTSELECTION_START},
+                          {"end", LOK_SETTEXTSELECTION_END},
+                          {"reset", LOK_SETTEXTSELECTION_RESET}},
+                         type) ||
+        !getTokenInteger(tokens[2], "x", x) ||
+        !getTokenInteger(tokens[3], "y", y))
+    {
+        sendTextFrame("error: cmd=selecttext kind=syntax");
+        return false;
+    }
+
+    _loKitDocument->pClass->setTextSelection(_loKitDocument, type, x, y);
+
+    return true;
+}
+
+bool ChildProcessSession::selectGraphic(const char *buffer, int length, StringTokenizer& tokens)
+{
+    int type, x, y;
+
+    if (tokens.count() != 4 ||
+        !getTokenKeyword(tokens[1], "type",
+                         {{"start", LOK_SETGRAPHICSELECTION_START},
+                          {"end", LOK_SETGRAPHICSELECTION_END}},
+                         type) ||
+        !getTokenInteger(tokens[2], "x", x) ||
+        !getTokenInteger(tokens[3], "y", y))
+    {
+        sendTextFrame("error: cmd=selectghraphic kind=syntax");
+        return false;
+    }
+
+    _loKitDocument->pClass->setGraphicSelection(_loKitDocument, type, x, y);
+
+    return true;
+}
+
+bool ChildProcessSession::resetSelection(const char *buffer, int length, StringTokenizer& tokens)
+{
+    if (tokens.count() != 1)
+    {
+        sendTextFrame("error: cmd=resetselection kind=syntax");
+        return false;
+    }
+
+    _loKitDocument->pClass->resetSelection(_loKitDocument);
+
+    return true;
+}
+
+bool ChildProcessSession::saveAs(const char *buffer, int length, StringTokenizer& tokens)
+{
+    std::string url, format, filterOptions;
+
+    if (tokens.count() < 4 ||
+        !getTokenString(tokens[1], "url", url))
+    {
+        sendTextFrame("error: cmd=saveas kind=syntax");
+        return false;
+    }
+
+    URI::decode(url, url, true);
+    if (getTokenString(tokens[2], "format", format)) {
+        URI::decode(format, format, true);
+    }
+
+    if (getTokenString(tokens[3], "options", filterOptions)) {
+        if (tokens.count() > 4) {
+            filterOptions += Poco::cat(std::string(" "), tokens.begin() + 4, tokens.end());
+        }
+    }
+
+    _loKitDocument->pClass->saveAs(_loKitDocument, url.c_str(), format.c_str(), filterOptions.c_str());
+
+    return true;
+}
+
+bool ChildProcessSession::setClientPart(const char *buffer, int length, StringTokenizer& tokens)
+{
+    if (tokens.count() < 2 ||
+        !getTokenInteger(tokens[1], "part", _clientPart))
+    {
+        return false;
+    }
+    return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/ChildProcessSession.hpp b/loolwsd/ChildProcessSession.hpp
new file mode 100644
index 0000000..7bf1f6d
--- /dev/null
+++ b/loolwsd/ChildProcessSession.hpp
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_LOOLCHILDPROCESSSESSION_HPP
+#define INCLUDED_LOOLCHILDPROCESSSESSION_HPP
+
+#define LOK_USE_UNSTABLE_API
+#include <LibreOfficeKit/LibreOfficeKit.h>
+
+#include <Poco/StringTokenizer.h>
+
+#include "LOOLSession.hpp"
+
+class ChildProcessSession final : public LOOLSession
+{
+public:
+    ChildProcessSession(std::shared_ptr<Poco::Net::WebSocket> ws, LibreOfficeKit *loKit);
+    virtual ~ChildProcessSession();
+
+    virtual bool handleInput(const char *buffer, int length) override;
+
+    virtual bool getStatus(const char *buffer, int length);
+
+    LibreOfficeKitDocument *_loKitDocument;
+
+ protected:
+    virtual bool loadDocument(const char *buffer, int length, Poco::StringTokenizer& tokens) override;
+
+    virtual void sendTile(const char *buffer, int length, Poco::StringTokenizer& tokens);
+
+    bool getTextSelection(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool keyEvent(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool mouseEvent(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool unoCommand(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool selectText(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool selectGraphic(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool resetSelection(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool saveAs(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool setClientPart(const char *buffer, int length, Poco::StringTokenizer& tokens);
+
+    std::string _jail;
+    std::string _loSubPath;
+    LibreOfficeKit *_loKit;
+
+ private:
+    int _clientPart;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/LOOLBroker.cpp b/loolwsd/LOOLBroker.cpp
new file mode 100644
index 0000000..3f50572
--- /dev/null
+++ b/loolwsd/LOOLBroker.cpp
@@ -0,0 +1,451 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/capability.h>
+
+#include <utime.h>
+#include <ftw.h>
+#include <unistd.h>
+
+#include <mutex>
+#include <cstring>
+#include <cassert>
+#include <iostream>
+#include <fstream>
+
+#include <Poco/Types.h>
+#include <Poco/Random.h>
+#include <Poco/Path.h>
+#include <Poco/File.h>
+#include <Poco/ThreadLocal.h>
+#include <Poco/Process.h>
+#include <Poco/Thread.h>
+#include <Poco/SharedMemory.h>
+
+#include "Util.hpp"
+
+using Poco::Path;
+using Poco::File;
+using Poco::ThreadLocal;
+using Poco::Process;
+using Poco::Thread;
+using Poco::ProcessHandle;
+
+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)
+            {
+                std::cout << Util::logPrefix() +
+                                                       "link(\"" + fpath + "\",\"" + newPath.toString() + "\") failed: " +
+                                                       strerror(errno) << std::endl;
+                exit(1);
+            }
+            break;
+        case FTW_DP:
+            {
+                struct stat st;
+                if (stat(fpath, &st) == -1)
+                {
+                    std::cout << Util::logPrefix() +
+                                                           "stat(\"" + fpath + "\") failed: " +
+                                                           strerror(errno) << std::endl;
+                    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)
+                {
+                    std::cout << Util::logPrefix() +
+                                                           "utime(\"" + newPath.toString() + "\", &ut) failed: " +
+                                                           strerror(errno) << std::endl;
+                    return 1;
+                }
+            }
+            break;
+        case FTW_DNR:
+            std::cout <<Util::logPrefix() +
+                                                   "Cannot read directory '" + fpath + "'" << std::endl;
+            return 1;
+        case FTW_NS:
+            std::cout <<Util::logPrefix() +
+                                                   "nftw: stat failed for '" + fpath + "'" << std::endl;
+            return 1;
+        case FTW_SLN:
+            std::cout <<Util::logPrefix() +
+                                                         "nftw: symlink to nonexistent file: '" + fpath + "', ignored" << std::endl;
+            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)
+            std::cout << Util::logPrefix() +
+                                                   "linkOrCopy: nftw() failed for '" + source + "'" << std::endl;
+    }
+
+    void dropCapability(
+#ifdef __linux
+                        cap_value_t capability
+#endif
+                        )
+    {
+#ifdef __linux
+        cap_t caps;
+        cap_value_t cap_list[] = { capability };
+
+        caps = cap_get_proc();
+        if (caps == NULL)
+        {
+            std::cout << Util::logPrefix() + "cap_get_proc() failed: " + strerror(errno) << std::endl;
+            exit(1);
+        }
+
+        if (cap_set_flag(caps, CAP_EFFECTIVE, sizeof(cap_list)/sizeof(cap_list[0]), cap_list, CAP_CLEAR) == -1 ||
+            cap_set_flag(caps, CAP_PERMITTED, sizeof(cap_list)/sizeof(cap_list[0]), cap_list, CAP_CLEAR) == -1)
+        {
+            std::cout << Util::logPrefix() +  "cap_set_flag() failed: " + strerror(errno) << std::endl;
+            exit(1);
+        }
+
+        if (cap_set_proc(caps) == -1)
+        {
+            std::cout << std::string("cap_set_proc() failed: ") + strerror(errno) << std::endl;
+            exit(1);
+        }
+
+        char *capText = cap_to_text(caps, NULL);
+        std::cout <<Util::logPrefix() + "Capabilities now: " + capText << std::endl;
+        cap_free(capText);
+
+        cap_free(caps);
+#endif
+        // We assume that on non-Linux we don't need to be root to be able to hardlink to files we
+        // don't own, so drop root.
+        if (geteuid() == 0 && getuid() != 0)
+        {
+            // The program is setuid root. Not normal on Linux where we use setcap, but if this
+            // needs to run on non-Linux Unixes, setuid root is what it will bneed to be to be able
+            // to do chroot().
+            if (setuid(getuid()) != 0) {
+                std::cout << std::string("setuid() failed: ") + strerror(errno) << std::endl;
+            }
+        }
+#if ENABLE_DEBUG
+        if (geteuid() == 0 && getuid() == 0)
+        {
+#ifdef __linux
+            // Argh, awful hack
+            if (capability == CAP_FOWNER)
+                return;
+#endif
+
+            // Running under sudo, probably because being debugged? Let's drop super-user rights.
+            LOOLWSD::runningAsRoot = true;
+            if (LOOLWSD::uid == 0)
+            {
+                struct passwd *nobody = getpwnam("nobody");
+                if (nobody)
+                    LOOLWSD::uid = nobody->pw_uid;
+                else
+                    LOOLWSD::uid = 65534;
+            }
+            if (setuid(LOOLWSD::uid) != 0) {
+                std::cout << std::string("setuid() failed: ") + strerror(errno) << std::endl;
+            }
+        }
+#endif
+    }
+}
+
+static std::map<Poco::Process::PID, Poco::UInt64> _childProcesses;
+
+
+static int prefixcmp(const char *str, const char *prefix)
+{
+	for (; ; str++, prefix++)
+		if (!*prefix)
+			return 0;
+		else if (*str != *prefix)
+			return (unsigned char)*prefix - (unsigned char)*str;
+}
+
+
+static int createLibreOfficeKit()
+{
+    Process::Args args;
+    //args.push_back("--losubpath=" + LOOLWSD::loSubPath);
+    //args.push_back("--systemplate=" + sysTemplate);
+    //args.push_back("--lotemplate=" + loTemplate);
+    //args.push_back("--childroot=" + childRoot);
+    //args.push_back("--numprespawns=" + std::to_string(_numPreSpawnedChildren));
+
+    std::string executable = "loolkit";
+    
+    /*if (!File(executable).exists())
+    {
+      std::cout << Util::logPrefix() + "Error loolkit does not exists" << std::endl;      
+      return -1;
+    }*/
+    
+	  //Process::Env env;
+    //env["LD_LIBRARY_PATH"] = "/usr/local/lib";
+    //env["LD_DEBUG"] = "libs";
+    
+    std::cout << Util::logPrefix() + "Launching LibreOfficeKit: " + executable + " " + Poco::cat(std::string(" "), args.begin(), args.end()) << std::endl;
+    
+    //ProcessHandle child = Process::launch(executable, args, "/usr/bin/", NULL, NULL, NULL, env);
+    ProcessHandle child = Process::launch(executable, args);
+    
+    _childProcesses[child.id()] = child.id();
+    return 0;
+}
+
+static void startupLibreOfficeKit(int nLOKits)
+{
+    for (int nCntr = nLOKits; nCntr; nCntr--)
+    {
+        if (createLibreOfficeKit() < 0)
+            break;
+    }
+}
+
+// Broker process
+int main(int argc, char** argv)
+{
+    // Initialization
+    std::mutex _rngMutex;
+    Poco::Random _rng;
+    std::string childRoot;
+    std::string loSubPath;
+    std::string sysTemplate;
+    std::string loTemplate;
+    int _numPreSpawnedChildren = 0;
+    Poco::SharedMemory _sharedForkChild("loolwsd", sizeof(bool), Poco::SharedMemory::AM_WRITE);
+    
+    while (argc > 0) 
+    {
+		  char *cmd = argv[0];
+		  char *eq  = NULL;
+		  if (strstr(cmd, "loolbroker"))
+      {
+        
+      }		  
+      if (!prefixcmp(cmd, "--losubpath=")) 
+      {
+        eq = strchrnul(cmd, '=');
+        if (*eq)
+          loSubPath = std::string(++eq);
+      }
+      else if (!prefixcmp(cmd, "--systemplate="))
+      {
+        eq = strchrnul(cmd, '=');
+        if (*eq)
+          sysTemplate = std::string(++eq);
+      }
+      else if (!prefixcmp(cmd, "--lotemplate="))
+      {
+        eq = strchrnul(cmd, '=');
+        if (*eq)
+          loTemplate = std::string(++eq);
+      }
+      else if (!prefixcmp(cmd, "--childroot="))
+      {
+        eq = strchrnul(cmd, '=');
+        if (*eq)
+          childRoot = std::string(++eq);
+      }
+      else if (!prefixcmp(cmd, "--numprespawns="))
+      {
+        eq = strchrnul(cmd, '=');
+        if (*eq)
+          _numPreSpawnedChildren = std::stoi(std::string(++eq));
+      }
+      
+		  argv++;
+		  argc--;
+    }
+   
+   if (loSubPath.empty())
+   {
+     std::cout << "--losubpath is empty" << std::endl;
+     exit(1);
+   }
+    
+   if (sysTemplate.empty())
+   {
+     std::cout << "--systemplate is empty" << std::endl;
+     exit(1);
+   }
+
+   if (loTemplate.empty())
+   {
+     std::cout << "--lotemplate is empty" << std::endl;
+     exit(1);
+   }
+
+   if (childRoot.empty())
+   {
+     std::cout << "--childroot is empty" << std::endl;
+     exit(1);
+   }
+
+   if ( !_numPreSpawnedChildren )
+   {
+     std::cout << "--numprespawns is 0" << std::endl;
+     exit(1);
+   }
+
+    std::unique_lock<std::mutex> rngLock(_rngMutex);
+    Poco::UInt64 _childId = (((Poco::UInt64)_rng.next()) << 32) | _rng.next() | 1;
+    rngLock.unlock();
+    
+
+    Path jail = Path::forDirectory(childRoot + Path::separator() + std::to_string(_childId));
+    File(jail).createDirectories();
+
+    Path jailLOInstallation(jail, loSubPath);
+    jailLOInstallation.makeDirectory();
+    File(jailLOInstallation).createDirectory();
+
+    // Copy (link) LO installation and other necessary files into it from the template
+
+    linkOrCopy(sysTemplate, jail);
+    linkOrCopy(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)
+    {
+        std::cout << Util::logPrefix() +
+                "mknod(" + jail.toString() + "/dev/random) failed: " +
+                strerror(errno) << std::endl;
+
+    }
+    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)
+    {
+        std::cout << Util::logPrefix() +
+                "mknod(" + jail.toString() + "/dev/urandom) failed: " +
+                strerror(errno) << std::endl;
+    }
+#endif
+
+    std::cout << "desktopMain -> chroot(\"" + jail.toString() + "\")" << std::endl;
+    if (chroot(jail.toString().c_str()) == -1)
+    {
+        std::cout << "chroot(\"" + jail.toString() + "\") failed: " + strerror(errno) << std::endl;
+        exit(-1);
+    }
+
+    if (chdir("/") == -1)
+    {
+        std::cout << std::string("chdir(\"/\") in jail failed: ") + strerror(errno) << std::endl;
+        exit(-1);
+    }
+
+#ifdef __linux
+    dropCapability(CAP_SYS_CHROOT);
+#else
+    dropCapability();
+#endif
+
+    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);
+    }
+
+    startupLibreOfficeKit(_numPreSpawnedChildren);
+
+    while (_childProcesses.size() > 0)
+    {
+        int status;
+        pid_t pid = waitpid(-1, &status, WUNTRACED | WNOHANG);
+        if (pid > 0)
+        {
+            if ( _childProcesses.find(pid) != _childProcesses.end() )
+            {
+                if ((WIFEXITED(status) || WIFSIGNALED(status) || WTERMSIG(status) ) )
+                {
+                    std::cout << Util::logPrefix() << "One of our known child processes died :" << std::to_string(pid)  << std::endl;
+                    _childProcesses.erase(pid);
+                }
+
+                if ( WCOREDUMP(status) )
+                    std::cout << Util::logPrefix() << "The child produced a core dump." << std::endl;
+
+                if ( WIFSTOPPED(status) )
+                    std::cout << Util::logPrefix() << "The child process was stopped by delivery of a signal." << std::endl;
+
+                if ( WSTOPSIG(status) )
+                    std::cout << Util::logPrefix() << "The child process was stopped." << std::endl;
+
+                if ( WIFCONTINUED(status) )
+                    std::cout << Util::logPrefix() << "The child process was resumed." << std::endl;
+            }
+            else
+            {
+                std::cout << Util::logPrefix() << "None of our known child processes died :" << std::to_string(pid) << std::endl;
+            }
+        }
+        else if (pid < 0)
+            std::cout << Util::logPrefix() << "Child error: " << strerror(errno) << std::endl;
+
+        if ( _sharedForkChild.begin()[0] )
+        {
+            _sharedForkChild.begin()[0] = 0;
+            std::cout << Util::logPrefix() << "No availabe child session, fork new one" << std::endl;
+            if (createLibreOfficeKit() < 0 )
+                break;
+        }
+    }
+    
+    // Terminate child processes
+    for (auto i : _childProcesses)
+    {
+        std::cout << Util::logPrefix() + "Requesting child process " + std::to_string(i.first) + " to terminate" << std::endl;
+        Process::requestTermination(i.first);
+    }
+    
+    std::cout << Util::logPrefix() << "loolbroker finished OK!" << std::endl;
+    return 0;
+}
diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
new file mode 100644
index 0000000..06f5dcf
--- /dev/null
+++ b/loolwsd/LOOLKit.cpp
@@ -0,0 +1,164 @@
+
+#include <memory>
+#include <iostream>
+
+#include <Poco/NamedMutex.h>
+#include <Poco/Util/Application.h>
+#include <Poco/Net/WebSocket.h>
+#include <Poco/Net/HTTPClientSession.h>
+#include <Poco/Net/HTTPRequest.h>
+#include <Poco/Net/HTTPResponse.h>
+#include <Poco/Thread.h>
+#include <Poco/Runnable.h>
+#include <Poco/StringTokenizer.h>
+#include <Poco/Exception.h>
+#include <Poco/Process.h>
+
+#define LOK_USE_UNSTABLE_API
+#include <LibreOfficeKit/LibreOfficeKitInit.h>
+
+#include "tsqueue.h"
+#include "Util.hpp"
+#include "ChildProcessSession.hpp"
+#include "LOOLProtocol.hpp"
+
+using namespace LOOLProtocol;
+using Poco::Util::Application;
+using Poco::Net::WebSocket;
+using Poco::Net::HTTPClientSession;
+using Poco::Net::HTTPRequest;
+using Poco::Net::HTTPResponse;
+using Poco::Thread;
+using Poco::Runnable;
+using Poco::StringTokenizer;
+using Poco::Exception;
+using Poco::Process;
+
+class QueueHandler: public Runnable
+{
+public:
+    QueueHandler(tsqueue<std::string>& queue):
+        _queue(queue)
+    {
+    }
+
+    void setSession(std::shared_ptr<LOOLSession> session)
+    {
+        _session = session;
+    }
+
+    void run() override
+    {
+        while (true)
+        {
+            std::string input = _queue.get();
+            if (input == "eof")
+                break;
+            if (!_session->handleInput(input.c_str(), input.size()))
+                break;
+        }
+    }
+
+private:
+    std::shared_ptr<LOOLSession> _session;
+    tsqueue<std::string>& _queue;
+};
+
+const int MASTER_PORT_NUMBER = 9981;
+const std::string CHILD_URI = "/loolws/child/";
+
+Poco::NamedMutex _namedMutexLOOL("loolwsd");
+
+int main(int argc, char** argv)
+{
+    std::string loSubPath = "lo";
+    Poco::UInt64 _childId = Process::id();
+
+    try
+    {
+        _namedMutexLOOL.lock();
+
+#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 (!loKit)
+        {
+            Application::instance().logger().fatal(Util::logPrefix() + "LibreOfficeKit initialisation failed");
+            exit(Application::EXIT_UNAVAILABLE);
+        }
+
+        _namedMutexLOOL.unlock();
+
+        // Open websocket connection between the child process and the
+        // parent. The parent forwards us requests that it can't handle.
+
+        HTTPClientSession cs("127.0.0.1", MASTER_PORT_NUMBER);
+        cs.setTimeout(0);
+        HTTPRequest request(HTTPRequest::HTTP_GET, CHILD_URI);
+        HTTPResponse response;
+        std::shared_ptr<WebSocket> ws(new WebSocket(cs, request, response));
+
+        std::shared_ptr<ChildProcessSession> session(new ChildProcessSession(ws, loKit));
+
+        ws->setReceiveTimeout(0);
+
+        std::string hello("child " + std::to_string(_childId));
+        session->sendTextFrame(hello);
+
+        tsqueue<std::string> queue;
+        Thread queueHandlerThread;
+        QueueHandler handler(queue);
+
+        handler.setSession(session);
+        queueHandlerThread.start(handler);
+
+        int flags;
+        int n;
+        do
+        {
+            char buffer[1024];
+            n = ws->receiveFrame(buffer, sizeof(buffer), flags);
+
+            if (n > 0 && (flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE)
+            {
+                std::string firstLine = getFirstLine(buffer, n);
+                StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+
+                // The only kind of messages a child process receives are the single-line ones (?)
+                assert(firstLine.size() == static_cast<std::string::size_type>(n));
+
+                // Check if it is a "canceltiles" and in that case remove outstanding
+                // "tile" messages from the queue.
+                if (tokens.count() == 1 && tokens[0] == "canceltiles")
+                {
+                    queue.remove_if([](std::string& x) {
+                        return (x.find("tile ") == 0 && x.find("id=") == std::string::npos);
+                    });
+                }
+                else
+                {
+                    queue.put(firstLine);
+                }
+            }
+        }
+        while (n > 0 && (flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE);
+
+        queue.clear();
+        queue.put("eof");
+        queueHandlerThread.join();
+    }
+    catch (Exception& exc)
+    {
+        Application::instance().logger().log(Util::logPrefix() + "Exception: " + exc.what());
+    }
+    catch (std::exception& exc)
+    {
+        Application::instance().logger().error(Util::logPrefix() + "Exception: " + exc.what());
+    }
+    
+    std::cout << Util::logPrefix() << "loolkit finished OK!" << std::endl;
+    return 0;
+}
diff --git a/loolwsd/LOOLSession.cpp b/loolwsd/LOOLSession.cpp
index 3294806..b0a7a05 100644
--- a/loolwsd/LOOLSession.cpp
+++ b/loolwsd/LOOLSession.cpp
@@ -24,58 +24,10 @@
 #include <mutex>
 #include <set>
 
-#define LOK_USE_UNSTABLE_API
-#include <LibreOfficeKit/LibreOfficeKit.h>
-#include <LibreOfficeKit/LibreOfficeKitEnums.h>
-
-#include <Poco/Exception.h>
-#include <Poco/File.h>
-#include <Poco/Net/HTTPStreamFactory.h>
-#include <Poco/Net/WebSocket.h>
-#include <Poco/Path.h>
-#include <Poco/Process.h>
-#include <Poco/Random.h>
-#include <Poco/StreamCopier.h>
-#include <Poco/String.h>
-#include <Poco/StringTokenizer.h>
-#include <Poco/ThreadLocal.h>
-#include <Poco/URI.h>
-#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"
 #include "LOOLSession.hpp"
-#include "LOOLWSD.hpp"
-#include "TileCache.hpp"
 #include "Util.hpp"
 
-using namespace LOOLProtocol;
-
-using Poco::File;
-using Poco::IOException;
-using Poco::Net::HTTPStreamFactory;
 using Poco::Net::WebSocket;
-using Poco::Path;
-using Poco::Process;
-using Poco::ProcessHandle;
-using Poco::Random;
-using Poco::StreamCopier;
-using Poco::StringTokenizer;
-using Poco::Thread;
-using Poco::ThreadLocal;
-using Poco::UInt64;
-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";
 
@@ -84,7 +36,7 @@ LOOLSession::LOOLSession(std::shared_ptr<WebSocket> ws, Kind kind) :
     _ws(ws),
     _docURL("")
 {
-    std::cout << Util::logPrefix() << "LOOLSession ctor this=" << this << " " << _kind << " ws=" << _ws.get() << std::endl;
+    //std::cout << Util::logPrefix() << "LOOLSession ctor this=" << this << " " << _kind << " ws=" << _ws.get() << std::endl;
     if (kind == Kind::ToClient) {
         _kindString = "ToClient";
     }
@@ -98,7 +50,7 @@ LOOLSession::LOOLSession(std::shared_ptr<WebSocket> ws, Kind kind) :
 
 LOOLSession::~LOOLSession()
 {
-    std::cout << Util::logPrefix() << "LOOLSession dtor this=" << this << " " << _kind << std::endl;
+    //std::cout << Util::logPrefix() << "LOOLSession dtor this=" << this << " " << _kind << std::endl;
     Util::shutdownWebSocket(*_ws);
 }
 
@@ -121,927 +73,4 @@ void LOOLSession::sendBinaryFrame(const char *buffer, int length)
 
     _ws->sendFrame(buffer, length, WebSocket::FRAME_BINARY);
 }
-
-std::map<Process::PID, UInt64> MasterProcessSession::_childProcesses;
-
-std::set<UInt64> MasterProcessSession::_pendingPreSpawnedChildren;
-std::set<std::shared_ptr<MasterProcessSession>> MasterProcessSession::_availableChildSessions;
-std::mutex MasterProcessSession::_availableChildSessionMutex;
-std::condition_variable MasterProcessSession::_availableChildSessionCV;
-Random MasterProcessSession::_rng;
-std::mutex MasterProcessSession::_rngMutex;
-
-MasterProcessSession::MasterProcessSession(std::shared_ptr<WebSocket> ws, Kind kind) :
-    LOOLSession(ws, kind),
-    _childId(0),
-    _curPart(0)
-{
-    std::cout << Util::logPrefix() << "MasterProcessSession ctor this=" << this << " ws=" << _ws.get() << std::endl;
-}
-
-MasterProcessSession::~MasterProcessSession()
-{
-    std::cout << Util::logPrefix() << "MasterProcessSession dtor this=" << this << " _peer=" << _peer.lock().get() << std::endl;
-    Util::shutdownWebSocket(*_ws);
-    auto peer = _peer.lock();
-    if (_kind == Kind::ToClient && peer)
-    {
-        Util::shutdownWebSocket(*(peer->_ws));
-    }
-}
-
-bool MasterProcessSession::handleInput(const char *buffer, int length)
-{
-    Application::instance().logger().information(Util::logPrefix() + _kindString + ",Input," + getAbbreviatedMessage(buffer, length));
-
-    std::string firstLine = getFirstLine(buffer, length);
-    StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
-
-    if (haveSeparateProcess())
-    {
-        // Note that this handles both forwarding requests from the client to the child process, and
-        // forwarding replies from the child process to the client. Or does it?
-
-        // Snoop at some  messages and manipulate tile cache information as needed
-        auto peer = _peer.lock();
-
-        if (_kind == Kind::ToPrisoner)
-        {
-            if (tokens[0] == "curpart:" &&
-                tokens.count() == 2 &&
-                getTokenInteger(tokens[1], "part", _curPart))
-            {
-                return true;
-            }
-        }
-
-        if (_kind == Kind::ToPrisoner && peer && peer->_tileCache)
-        {
-            if (tokens[0] == "tile:")
-            {
-                int part, width, height, tilePosX, tilePosY, tileWidth, tileHeight;
-                if (tokens.count() < 8 ||
-                    !getTokenInteger(tokens[1], "part", part) ||
-                    !getTokenInteger(tokens[2], "width", width) ||
-                    !getTokenInteger(tokens[3], "height", height) ||
-                    !getTokenInteger(tokens[4], "tileposx", tilePosX) ||
-                    !getTokenInteger(tokens[5], "tileposy", tilePosY) ||
-                    !getTokenInteger(tokens[6], "tilewidth", tileWidth) ||
-                    !getTokenInteger(tokens[7], "tileheight", tileHeight))
-                    assert(false);
-
-                assert(firstLine.size() < static_cast<std::string::size_type>(length));
-                peer->_tileCache->saveTile(part, width, height, tilePosX, tilePosY, tileWidth, tileHeight, buffer + firstLine.size() + 1, length - firstLine.size() - 1);
-            }
-            else if (tokens[0] == "status:")
-            {
-                peer->_tileCache->saveStatus(firstLine);
-            }
-            else if (tokens[0] == "invalidatetiles:")
-            {
-                // FIXME temporarily, set the editing on the 1st invalidate, TODO extend
-                // the protocol so that the client can set the editing or view only.
-                peer->_tileCache->setEditing(true);
-
-                assert(firstLine.size() == static_cast<std::string::size_type>(length));
-                peer->_tileCache->invalidateTiles(firstLine);
-            }
-        }
-
-        forwardToPeer(buffer, length);
-        return true;
-    }
-
-    if (tokens[0] == "child")
-    {
-        if (_kind != Kind::ToPrisoner)
-        {
-            sendTextFrame("error: cmd=child kind=invalid");
-            return false;
-        }
-        if (!_peer.expired())
-        {
-            sendTextFrame("error: cmd=child kind=invalid");
-            return false;
-        }
-        if (tokens.count() != 2)
-        {
-            sendTextFrame("error: cmd=child kind=syntax");
-            return false;
-        }
-
-        UInt64 childId = std::stoull(tokens[1]);
-        // 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);
-        }
-        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;
-        _childId = childId;
-        lock.unlock();
-        _availableChildSessionCV.notify_one();
-    }
-    else if (_kind == Kind::ToPrisoner)
-    {
-        // Message from child process to be forwarded to client.
-
-        // I think we should never get here
-        assert(false);
-    }
-    else if (tokens[0] == "load")
-    {
-        if (_docURL != "")
-        {
-            sendTextFrame("error: cmd=load kind=docalreadyloaded");
-            return false;
-        }
-        return loadDocument(buffer, length, tokens);
-    }
-    else if (tokens[0] != "canceltiles" &&
-             tokens[0] != "gettextselection" &&
-             tokens[0] != "invalidatetiles" &&
-             tokens[0] != "key" &&
-             tokens[0] != "mouse" &&
-             tokens[0] != "resetselection" &&
-             tokens[0] != "saveas" &&
-             tokens[0] != "selectgraphic" &&
-             tokens[0] != "selecttext" &&
-             tokens[0] != "setclientpart" &&
-             tokens[0] != "status" &&
-             tokens[0] != "tile" &&
-             tokens[0] != "uno")
-    {
-        sendTextFrame("error: cmd=" + tokens[0] + " kind=unknown");
-        return false;
-    }
-    else if (_docURL == "")
-    {
-        sendTextFrame("error: cmd=" + tokens[0] + " kind=nodocloaded");
-        return false;
-    }
-    else if (tokens[0] == "canceltiles")
-    {
-        if (!_peer.expired())
-            forwardToPeer(buffer, length);
-    }
-    else if (tokens[0] == "invalidatetiles")
-    {
-        return invalidateTiles(buffer, length, tokens);
-    }
-    else if (tokens[0] == "status")
-    {
-        return getStatus(buffer, length);
-    }
-    else if (tokens[0] == "tile")
-    {
-        sendTile(buffer, length, tokens);
-    }
-    else
-    {
-        // All other commands are such that they always require a
-        // LibreOfficeKitDocument session, i.e. need to be handled in
-        // a child process.
-
-        if (_peer.expired())
-            dispatchChild();
-        forwardToPeer(buffer, length);
-
-        if ((tokens.count() > 1 && tokens[0] == "uno" && tokens[1] == ".uno:Save") ||
-               tokens[0] == "saveas") {
-           _tileCache->documentSaved();
-        }
-    }
-    return true;
-}
-
-bool MasterProcessSession::haveSeparateProcess()
-{
-    return _childId != 0;
-}
-
-Path MasterProcessSession::getJailPath(UInt64 childId)
-{
-    return Path::forDirectory(LOOLWSD::childRoot + Path::separator() + std::to_string(childId));
-}
-
-void MasterProcessSession::addPendingChildrem(UInt64 childId)
-{
-    _pendingPreSpawnedChildren.insert(childId);
-}
-
-int  MasterProcessSession::getAvailableChildSessions()
-{
-    return _availableChildSessions.size();
-}
-
-int  MasterProcessSession::getPendingPreSpawnedChildren()
-{
-    return _pendingPreSpawnedChildren.size();
-}
-
-
-bool MasterProcessSession::invalidateTiles(const char *buffer, int length, StringTokenizer& tokens)
-{
-    int part, tilePosX, tilePosY, tileWidth, tileHeight;
-
-    if (tokens.count() != 6 ||
-        !getTokenInteger(tokens[1], "part", part) ||
-        !getTokenInteger(tokens[2], "tileposx", tilePosX) ||
-        !getTokenInteger(tokens[3], "tileposy", tilePosY) ||
-        !getTokenInteger(tokens[4], "tilewidth", tileWidth) ||
-        !getTokenInteger(tokens[5], "tileheight", tileHeight))
-    {
-        sendTextFrame("error: cmd=invalidatetiles kind=syntax");
-        return false;
-    }
-
-    // FIXME temporarily, set the editing on the 1st invalidate, TODO extend
-    // the protocol so that the client can set the editing or view only.
-    _tileCache->setEditing(true);
-
-    _tileCache->invalidateTiles(_curPart, tilePosX, tilePosY, tileWidth, tileHeight);
-    return true;
-}
-
-bool MasterProcessSession::loadDocument(const char *buffer, int length, StringTokenizer& tokens)
-{
-    if (tokens.count() != 2)
-    {
-        sendTextFrame("error: cmd=load kind=syntax");
-        return false;
-    }
-
-    if (tokens[1].find("url=") == 0)
-        _docURL = tokens[1].substr(strlen("url="));
-    else
-        _docURL = tokens[1];
-
-    try
-    {
-        URI aUri(_docURL);
-    }
-    catch(Poco::SyntaxException&)
-    {
-        sendTextFrame("error: cmd=load kind=URI invalid syntax");
-        return false;
-    }
-
-    _tileCache.reset(new TileCache(_docURL));
-
-    return true;
-}
-
-bool MasterProcessSession::getStatus(const char *buffer, int length)
-{
-    std::string status;
-
-    status = _tileCache->getStatus();
-    if (status.size() > 0)
-    {
-        sendTextFrame(status);
-        return true;
-    }
-
-    if (_peer.expired())
-        dispatchChild();
-    forwardToPeer(buffer, length);
-    return true;
-}
-
-void MasterProcessSession::sendTile(const char *buffer, int length, StringTokenizer& tokens)
-{
-    int part, width, height, tilePosX, tilePosY, tileWidth, tileHeight;
-
-    if (tokens.count() < 8 ||
-        !getTokenInteger(tokens[1], "part", part) ||
-        !getTokenInteger(tokens[2], "width", width) ||
-        !getTokenInteger(tokens[3], "height", height) ||
-        !getTokenInteger(tokens[4], "tileposx", tilePosX) ||
-        !getTokenInteger(tokens[5], "tileposy", tilePosY) ||
-        !getTokenInteger(tokens[6], "tilewidth", tileWidth) ||
-        !getTokenInteger(tokens[7], "tileheight", tileHeight))
-    {
-        sendTextFrame("error: cmd=tile kind=syntax");
-        return;
-    }
-
-    if (part < 0 ||
-        width <= 0 ||
-        height <= 0 ||
-        tilePosX < 0 ||
-        tilePosY < 0 ||
-        tileWidth <= 0 ||
-        tileHeight <= 0)
-    {
-        sendTextFrame("error: cmd=tile kind=invalid");
-        return;
-    }
-
-    std::string response = "tile: " + Poco::cat(std::string(" "), tokens.begin() + 1, tokens.end()) + "\n";
-
-    std::vector<char> output;
-    output.reserve(4 * width * height);
-    output.resize(response.size());
-    std::memcpy(output.data(), response.data(), response.size());
-
-    std::unique_ptr<std::fstream> cachedTile = _tileCache->lookupTile(part, width, height, tilePosX, tilePosY, tileWidth, tileHeight);
-    if (cachedTile && cachedTile->is_open())
-    {
-        cachedTile->seekg(0, std::ios_base::end);
-        size_t pos = output.size();
-        std::streamsize size = cachedTile->tellg();
-        output.resize(pos + size);
-        cachedTile->seekg(0, std::ios_base::beg);
-        cachedTile->read(output.data() + pos, size);
-        cachedTile->close();
-
-        sendBinaryFrame(output.data(), output.size());
-
-        return;
-    }
-
-    if (_peer.expired())
-        dispatchChild();
-    forwardToPeer(buffer, length);
-}
-
-void MasterProcessSession::dispatchChild()
-{
-    // Copy document into jail using the fixed name
-
-    std::shared_ptr<MasterProcessSession> childSession;
-    std::unique_lock<std::mutex> lock(_availableChildSessionMutex);
-
-    std::cout << Util::logPrefix() << "_availableChildSessions size=" << _availableChildSessions.size() << " _pendingChildSessions size=" << _pendingPreSpawnedChildren.size() << std::endl;
-
-    if (_availableChildSessions.size() == 0)
-    {
-        if (_pendingPreSpawnedChildren.size() == 0)
-        {
-            // 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");
-        }
-
-        std::cout << Util::logPrefix() << "waiting for a child session to become available" << std::endl;
-        _availableChildSessionCV.wait(lock, [] { return _availableChildSessions.size() > 0; });
-        std::cout << Util::logPrefix() << "waiting done" << std::endl;
-    }
-
-    childSession = *(_availableChildSessions.begin());
-
-    _availableChildSessions.erase(childSession);
-    std::cout << Util::logPrefix() << "_availableChildSessions size=" << _availableChildSessions.size() << std::endl;
-    if (_availableChildSessions.size() == 0)
-        LOOLWSD::_sharedForkChild.begin()[0] = 1;
-    lock.unlock();
-
-    // Assume a valid URI
-    URI aUri(_docURL);
-
-    if (aUri.isRelative())
-        aUri = URI( URI("file://"), aUri.toString() );
-
-    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());
-
-        try
-        {
-            File(aDstPath).createDirectories();
-        }
-        catch (Exception& exc)
-        {
-            Application::instance().logger().error( Util::logPrefix() +
-                "createDirectories(\"" + aDstPath.toString() + "\") 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;
-    childSession->_peer = shared_from_this();
-
-    std::string loadRequest = "load url=" + _docURL;
-    forwardToPeer(loadRequest.c_str(), loadRequest.size());
-}
-
-void MasterProcessSession::forwardToPeer(const char *buffer, int length)
-{
-    Application::instance().logger().information(Util::logPrefix() + _kindString + ",forwardToPeer," + getAbbreviatedMessage(buffer, length));
-    auto peer = _peer.lock();
-    if (!peer)
-        return;
-    peer->sendBinaryFrame(buffer, length);
-}
-
-ChildProcessSession::ChildProcessSession(std::shared_ptr<WebSocket> ws, LibreOfficeKit *loKit) :
-    LOOLSession(ws, Kind::ToMaster),
-    _loKitDocument(NULL),
-    _loKit(loKit),
-    _clientPart(0)
-{
-    std::cout << Util::logPrefix() << "ChildProcessSession ctor this=" << this << " ws=" << _ws.get() << std::endl;
-}
-
-ChildProcessSession::~ChildProcessSession()
-{
-    std::cout << Util::logPrefix() << "ChildProcessSession dtor this=" << this << std::endl;
-    if (LIBREOFFICEKIT_HAS(_loKit, registerCallback))
-        _loKit->pClass->registerCallback(_loKit, 0, 0);
-    Util::shutdownWebSocket(*_ws);
-}
-
-bool ChildProcessSession::handleInput(const char *buffer, int length)
-{
-    std::string firstLine = getFirstLine(buffer, length);
-    StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
-
-    Application::instance().logger().information(Util::logPrefix() + _kindString + ",Input," + getAbbreviatedMessage(buffer, length));
-
-    if (tokens[0] == "load")
-    {
-        if (_docURL != "")
-        {
-            sendTextFrame("error: cmd=load kind=docalreadyloaded");
-            return false;
-        }
-        return loadDocument(buffer, length, tokens);
-    }
-    else if (_docURL == "")
-    {
-        sendTextFrame("error: cmd=" + tokens[0] + " kind=nodocloaded");
-        return false;
-    }
-    else if (tokens[0] == "setclientpart")
-    {
-        return setClientPart(buffer, length, tokens);
-    }
-    else if (tokens[0] == "status")
-    {
-        return getStatus(buffer, length);
-    }
-    else if (tokens[0] == "tile")
-    {
-        sendTile(buffer, length, tokens);
-    }
-    else
-    {
-        // All other commands are such that they always require a LibreOfficeKitDocument session,
-        // i.e. need to be handled in a child process.
-
-        assert(tokens[0] == "gettextselection" ||
-               tokens[0] == "key" ||
-               tokens[0] == "mouse" ||
-               tokens[0] == "uno" ||
-               tokens[0] == "selecttext" ||
-               tokens[0] == "selectgraphic" ||
-               tokens[0] == "resetselection" ||
-               tokens[0] == "saveas");
-
-        if (_loKitDocument->pClass->getPart(_loKitDocument) != _clientPart)
-        {
-            _loKitDocument->pClass->setPart(_loKitDocument, _clientPart);
-        }
-        if (tokens[0] == "gettextselection")
-        {
-            return getTextSelection(buffer, length, tokens);
-        }
-        else if (tokens[0] == "key")
-        {
-            return keyEvent(buffer, length, tokens);
-        }
-        else if (tokens[0] == "mouse")
-        {
-            return mouseEvent(buffer, length, tokens);
-        }
-        else if (tokens[0] == "uno")
-        {
-            return unoCommand(buffer, length, tokens);
-        }
-        else if (tokens[0] == "selecttext")
-        {
-            return selectText(buffer, length, tokens);
-        }
-        else if (tokens[0] == "selectgraphic")
-        {
-            return selectGraphic(buffer, length, tokens);
-        }
-        else if (tokens[0] == "resetselection")
-        {
-            return resetSelection(buffer, length, tokens);
-        }
-        else if (tokens[0] == "saveas")
-        {
-            return saveAs(buffer, length, tokens);
-        }
-        else
-        {
-            assert(false);
-        }
-    }
-    return true;
-}
-
-extern "C"
-{
-    static void myCallback(int nType, const char* pPayload, void* pData)
-    {
-        ChildProcessSession *srv = reinterpret_cast<ChildProcessSession *>(pData);
-
-        switch ((LibreOfficeKitCallbackType) nType)
-        {
-        case LOK_CALLBACK_INVALIDATE_TILES:
-            {
-                int curPart = srv->_loKitDocument->pClass->getPart(srv->_loKitDocument);
-                srv->sendTextFrame("curpart: part=" + std::to_string(curPart));
-                StringTokenizer tokens(std::string(pPayload), " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
-                if (tokens.count() == 4)
-                {
-                    int x(std::stoi(tokens[0]));
-                    int y(std::stoi(tokens[1]));
-                    int width(std::stoi(tokens[2]));
-                    int height(std::stoi(tokens[3]));
-                    srv->sendTextFrame("invalidatetiles:"
-                                       " part=" + std::to_string(curPart) +
-                                       " x=" + std::to_string(x) +
-                                       " y=" + std::to_string(y) +
-                                       " width=" + std::to_string(width) +
-                                       " height=" + std::to_string(height));
-                }
-                else {
-                    srv->sendTextFrame("invalidatetiles: " + std::string(pPayload));
-                }
-            }
-            break;
-        case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
-            srv->sendTextFrame("invalidatecursor: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_TEXT_SELECTION:
-            srv->sendTextFrame("textselection: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_TEXT_SELECTION_START:
-            srv->sendTextFrame("textselectionstart: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_TEXT_SELECTION_END:
-            srv->sendTextFrame("textselectionend: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_CURSOR_VISIBLE:
-            srv->sendTextFrame("cursorvisible: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_GRAPHIC_SELECTION:
-            srv->sendTextFrame("graphicselection: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_HYPERLINK_CLICKED:
-            srv->sendTextFrame("hyperlinkclicked: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_STATE_CHANGED:
-            srv->sendTextFrame("statechanged: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_STATUS_INDICATOR_START:
-            srv->sendTextFrame("statusindicatorstart:");
-            break;
-        case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
-            srv->sendTextFrame("statusindicatorsetvalue: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_STATUS_INDICATOR_FINISH:
-            srv->sendTextFrame("statusindicatorfinish:");
-            break;
-        case LOK_CALLBACK_SEARCH_NOT_FOUND:
-            srv->sendTextFrame("searchnotfound: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
-            srv->getStatus("", 0);
-            break;
-        case LOK_CALLBACK_SET_PART:
-            srv->sendTextFrame("setpart: " + std::string(pPayload));
-            break;
-        }
-    }
-}
-
-bool ChildProcessSession::loadDocument(const char *buffer, int length, StringTokenizer& tokens)
-{
-    if (tokens.count() != 2)
-    {
-        sendTextFrame("error: cmd=load kind=syntax");
-        return false;
-    }
-
-    if (tokens[1].find("url=") == 0)
-        _docURL = tokens[1].substr(strlen("url="));
-    else
-        _docURL = tokens[1];
-
-    URI aUri;
-    try
-    {
-        aUri = URI(_docURL);
-    }
-    catch(Poco::SyntaxException&)
-    {
-        sendTextFrame("error: cmd=load kind=URI invalid syntax");
-        return false;
-    }
-
-    if (aUri.empty())
-    {
-        sendTextFrame("error: cmd=load kind=URI empty");
-        return false;
-    }
-
-    // The URL in the request is the original one, not visible in the chroot jail.
-    // The child process uses the fixed name jailDocumentURL.
-
-    if (LIBREOFFICEKIT_HAS(_loKit, registerCallback))
-        _loKit->pClass->registerCallback(_loKit, myCallback, this);
-
-    if (aUri.isRelative() || aUri.getScheme() == "file")
-        aUri = URI( URI("file://"), Path(jailDocumentURL, Path(aUri.getPath()).getFileName()).toString() );
-
-    if ((_loKitDocument = _loKit->pClass->documentLoad(_loKit, aUri.toString().c_str())) == NULL)
-    {
-        sendTextFrame("error: cmd=load kind=failed");
-        return false;
-    }
-
-    _loKitDocument->pClass->initializeForRendering(_loKitDocument);
-
-    if (!getStatus(buffer, length))
-        return false;
-    _loKitDocument->pClass->registerCallback(_loKitDocument, myCallback, this);
-
-    return true;
-}
-
-bool ChildProcessSession::getStatus(const char *buffer, int length)
-{
-    std::string status = "status: " + LOKitHelper::documentStatus(_loKitDocument);
-
-    sendTextFrame(status);
-
-    return true;
-}
-
-void ChildProcessSession::sendTile(const char *buffer, int length, StringTokenizer& tokens)
-{
-    int part, width, height, tilePosX, tilePosY, tileWidth, tileHeight;
-
-    if (tokens.count() < 8 ||
-        !getTokenInteger(tokens[1], "part", part) ||
-        !getTokenInteger(tokens[2], "width", width) ||
-        !getTokenInteger(tokens[3], "height", height) ||
-        !getTokenInteger(tokens[4], "tileposx", tilePosX) ||
-        !getTokenInteger(tokens[5], "tileposy", tilePosY) ||
-        !getTokenInteger(tokens[6], "tilewidth", tileWidth) ||
-        !getTokenInteger(tokens[7], "tileheight", tileHeight))
-    {
-        sendTextFrame("error: cmd=tile kind=syntax");
-        return;
-    }
-
-    if (part < 0 ||
-        width <= 0 ||
-        height <= 0 ||
-        tilePosX < 0 ||
-        tilePosY < 0 ||
-        tileWidth <= 0 ||
-        tileHeight <= 0)
-    {
-        sendTextFrame("error: cmd=tile kind=invalid");
-        return;
-    }
-
-    std::string response = "tile: " + Poco::cat(std::string(" "), tokens.begin() + 1, tokens.end()) + "\n";
-
-    std::vector<char> output;
-    output.reserve(4 * width * height);
-    output.resize(response.size());
-    std::memcpy(output.data(), response.data(), response.size());
-
-    unsigned char *pixmap = new unsigned char[4 * width * height];
-    _loKitDocument->pClass->setPart(_loKitDocument, part);
-    _loKitDocument->pClass->paintTile(_loKitDocument, pixmap, width, height, tilePosX, tilePosY, tileWidth, tileHeight);
-
-    if (!Util::encodePNGAndAppendToBuffer(pixmap, width, height, output))
-    {
-        sendTextFrame("error: cmd=tile kind=failure");
-        return;
-    }
-
-    delete[] pixmap;
-
-    sendBinaryFrame(output.data(), output.size());
-}
-
-bool ChildProcessSession::getTextSelection(const char *buffer, int length, StringTokenizer& tokens)
-{
-    std::string mimeType;
-
-    if (tokens.count() != 2 ||
-        !getTokenString(tokens[1], "mimetype", mimeType))
-    {
-        sendTextFrame("error: cmd=gettextselection kind=syntax");
-        return false;
-    }
-
-    char *textSelection = _loKitDocument->pClass->getTextSelection(_loKitDocument, mimeType.c_str(), NULL);
-
-    sendTextFrame("textselectioncontent: " + std::string(textSelection));
-    return true;
-}
-
-bool ChildProcessSession::keyEvent(const char *buffer, int length, StringTokenizer& tokens)
-{
-    int type, charcode, keycode;
-
-    if (tokens.count() != 4 ||
-        !getTokenKeyword(tokens[1], "type",
-                         {{"input", LOK_KEYEVENT_KEYINPUT}, {"up", LOK_KEYEVENT_KEYUP}},
-                         type) ||
-        !getTokenInteger(tokens[2], "char", charcode) ||
-        !getTokenInteger(tokens[3], "key", keycode))
-    {
-        sendTextFrame("error: cmd=key kind=syntax");
-        return false;
-    }
-
-    _loKitDocument->pClass->postKeyEvent(_loKitDocument, type, charcode, keycode);
-
-    return true;
-}
-
-bool ChildProcessSession::mouseEvent(const char *buffer, int length, StringTokenizer& tokens)
-{
-    int type, x, y, count;
-
-    if (tokens.count() != 5 ||
-        !getTokenKeyword(tokens[1], "type",
-                         {{"buttondown", LOK_MOUSEEVENT_MOUSEBUTTONDOWN},
-                          {"buttonup", LOK_MOUSEEVENT_MOUSEBUTTONUP},
-                          {"move", LOK_MOUSEEVENT_MOUSEMOVE}},
-                         type) ||
-        !getTokenInteger(tokens[2], "x", x) ||
-        !getTokenInteger(tokens[3], "y", y) ||
-        !getTokenInteger(tokens[4], "count", count))
-    {
-        sendTextFrame("error: cmd=mouse kind=syntax");
-        return false;
-    }
-
-    _loKitDocument->pClass->postMouseEvent(_loKitDocument, type, x, y, count);
-
-    return true;
-}
-
-bool ChildProcessSession::unoCommand(const char *buffer, int length, StringTokenizer& tokens)
-{
-    if (tokens.count() == 1)
-    {
-        sendTextFrame("error: cmd=uno kind=syntax");
-        return false;
-    }
-
-    if (tokens.count() == 2)
-    {
-        _loKitDocument->pClass->postUnoCommand(_loKitDocument, tokens[1].c_str(), 0);
-    }
-    else
-    {
-        _loKitDocument->pClass->postUnoCommand(_loKitDocument, tokens[1].c_str(), Poco::cat(std::string(" "), tokens.begin() + 2, tokens.end()).c_str());
-    }
-
-    return true;
-}
-
-bool ChildProcessSession::selectText(const char *buffer, int length, StringTokenizer& tokens)
-{
-    int type, x, y;
-
-    if (tokens.count() != 4 ||
-        !getTokenKeyword(tokens[1], "type",
-                         {{"start", LOK_SETTEXTSELECTION_START},
-                          {"end", LOK_SETTEXTSELECTION_END},
-                          {"reset", LOK_SETTEXTSELECTION_RESET}},
-                         type) ||
-        !getTokenInteger(tokens[2], "x", x) ||
-        !getTokenInteger(tokens[3], "y", y))
-    {
-        sendTextFrame("error: cmd=selecttext kind=syntax");
-        return false;
-    }
-
-    _loKitDocument->pClass->setTextSelection(_loKitDocument, type, x, y);
-
-    return true;
-}
-
-bool ChildProcessSession::selectGraphic(const char *buffer, int length, StringTokenizer& tokens)
-{
-    int type, x, y;
-
-    if (tokens.count() != 4 ||
-        !getTokenKeyword(tokens[1], "type",
-                         {{"start", LOK_SETGRAPHICSELECTION_START},
-                          {"end", LOK_SETGRAPHICSELECTION_END}},
-                         type) ||
-        !getTokenInteger(tokens[2], "x", x) ||
-        !getTokenInteger(tokens[3], "y", y))
-    {
-        sendTextFrame("error: cmd=selectghraphic kind=syntax");
-        return false;
-    }
-
-    _loKitDocument->pClass->setGraphicSelection(_loKitDocument, type, x, y);
-
-    return true;
-}
-
-bool ChildProcessSession::resetSelection(const char *buffer, int length, StringTokenizer& tokens)
-{
-    if (tokens.count() != 1)
-    {
-        sendTextFrame("error: cmd=resetselection kind=syntax");
-        return false;
-    }
-
-    _loKitDocument->pClass->resetSelection(_loKitDocument);
-
-    return true;
-}
-
-bool ChildProcessSession::saveAs(const char *buffer, int length, StringTokenizer& tokens)
-{
-    std::string url, format, filterOptions;
-
-    if (tokens.count() < 4 ||
-        !getTokenString(tokens[1], "url", url))
-    {
-        sendTextFrame("error: cmd=saveas kind=syntax");
-        return false;
-    }
-
-    URI::decode(url, url, true);
-    if (getTokenString(tokens[2], "format", format)) {
-        URI::decode(format, format, true);
-    }
-
-    if (getTokenString(tokens[3], "options", filterOptions)) {
-        if (tokens.count() > 4) {
-            filterOptions += Poco::cat(std::string(" "), tokens.begin() + 4, tokens.end());
-        }
-    }
-
-    _loKitDocument->pClass->saveAs(_loKitDocument, url.c_str(), format.c_str(), filterOptions.c_str());
-
-    return true;
-}
-
-bool ChildProcessSession::setClientPart(const char *buffer, int length, StringTokenizer& tokens)
-{
-    if (tokens.count() < 2 ||
-        !getTokenInteger(tokens[1], "part", _clientPart))
-    {
-        return false;
-    }
-    return true;
-}
-
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/LOOLSession.hpp b/loolwsd/LOOLSession.hpp
index 361f6fb..bd770c1 100644
--- a/loolwsd/LOOLSession.hpp
+++ b/loolwsd/LOOLSession.hpp
@@ -18,18 +18,8 @@
 #include <ostream>
 #include <set>
 
-#define LOK_USE_UNSTABLE_API
-#include <LibreOfficeKit/LibreOfficeKit.h>
-
 #include <Poco/Net/WebSocket.h>
-#include <Poco/Buffer.h>
-#include <Poco/Path.h>
-#include <Poco/Process.h>
-#include <Poco/Random.h>
 #include <Poco/StringTokenizer.h>
-#include <Poco/Types.h>
-
-#include "TileCache.hpp"
 
 // We have three kinds of Websocket sessions
 // 1) Between the master loolwsd server to the end-user LOOL client
@@ -93,98 +83,6 @@ inline std::basic_ostream<charT, traits> & operator <<(std::basic_ostream<charT,
     }
 }
 
-class MasterProcessSession final : public LOOLSession, public std::enable_shared_from_this<MasterProcessSession>
-{
-public:
-    MasterProcessSession(std::shared_ptr<Poco::Net::WebSocket> ws, Kind kind);
-    virtual ~MasterProcessSession();
-
-    virtual bool handleInput(const char *buffer, int length) override;
-
-    bool haveSeparateProcess();
-
-    static Poco::Path getJailPath(Poco::UInt64 childId);
-    static void addPendingChildrem(Poco::UInt64 childId);
-    static int  getAvailableChildSessions();
-    static int  getPendingPreSpawnedChildren();
-
-    static std::map<Poco::Process::PID, Poco::UInt64> _childProcesses;
-
-    virtual bool getStatus(const char *buffer, int length);
-
- protected:
-    bool invalidateTiles(const char *buffer, int length, Poco::StringTokenizer& tokens);
-
-    virtual bool loadDocument(const char *buffer, int length, Poco::StringTokenizer& tokens) override;
-
-    virtual void sendTile(const char *buffer, int length, Poco::StringTokenizer& tokens);
-
-    void dispatchChild();
-    void forwardToPeer(const char *buffer, int length);
-
-    // If _kind==ToPrisoner and the child process has started and completed its handshake with the
-    // parent process: Points to the WebSocketSession for the child process handling the document in
-    // question, if any.
-
-    // In the session to the child process, points to the LOOLSession for the LOOL client. This will
-    // obvious have to be rethought when we add collaboration and there can be several LOOL clients
-    // per document being edited (i.e., per child process).
-    std::weak_ptr<MasterProcessSession> _peer;
-
-    // Pre-spawned child processes that haven't yet connected.
-    static std::set<Poco::UInt64> _pendingPreSpawnedChildren;
-
-    // Sessions to pre-spawned child processes that have connected but are not yet assigned a
-    // document to work on.
-    static std::set<std::shared_ptr<MasterProcessSession>> _availableChildSessions;
-    static std::mutex _availableChildSessionMutex;
-    static std::condition_variable _availableChildSessionCV;
-
-    std::unique_ptr<TileCache> _tileCache;
-
-private:
-    // The id of the child process
-    Poco::UInt64 _childId;
-    static Poco::Random _rng;
-    static std::mutex _rngMutex;
-    int _curPart;
-};
-
-class ChildProcessSession final : public LOOLSession
-{
-public:
-    ChildProcessSession(std::shared_ptr<Poco::Net::WebSocket> ws, LibreOfficeKit *loKit);
-    virtual ~ChildProcessSession();
-
-    virtual bool handleInput(const char *buffer, int length) override;
-
-    virtual bool getStatus(const char *buffer, int length);
-
-    LibreOfficeKitDocument *_loKitDocument;
-
- protected:
-    virtual bool loadDocument(const char *buffer, int length, Poco::StringTokenizer& tokens) override;
-
-    virtual void sendTile(const char *buffer, int length, Poco::StringTokenizer& tokens);
-
-    bool getTextSelection(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool keyEvent(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool mouseEvent(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool unoCommand(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool selectText(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool selectGraphic(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool resetSelection(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool saveAs(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool setClientPart(const char *buffer, int length, Poco::StringTokenizer& tokens);
-
-    std::string _jail;
-    std::string _loSubPath;
-    LibreOfficeKit *_loKit;
-
- private:
-    int _clientPart;
-};
-
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index 4bccf72..ee36e0b 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -56,6 +56,7 @@ DEALINGS IN THE SOFTWARE.
 #include <cassert>
 #include <cstdlib>
 #include <cstring>
+#include <string>
 #include <iostream>
 #include <sstream>
 #include <mutex>
@@ -96,7 +97,7 @@ DEALINGS IN THE SOFTWARE.
 
 
 #include "LOOLProtocol.hpp"
-#include "LOOLSession.hpp"
+#include "MasterProcessSession.hpp"
 #include "LOOLWSD.hpp"
 #include "tsqueue.h"
 #include "Util.hpp"
@@ -138,6 +139,84 @@ using Poco::Net::Socket;
 using Poco::ThreadLocal;
 using Poco::Random;
 using Poco::NamedMutex;
+using Poco::ProcessHandle;
+
+
+namespace
+{
+    void dropCapability(
+#ifdef __linux
+                        cap_value_t capability
+#endif
+                        )
+    {
+#ifdef __linux
+        cap_t caps;
+        cap_value_t cap_list[] = { capability };
+
+        caps = cap_get_proc();
+        if (caps == NULL)
+        {
+            Application::instance().logger().error(Util::logPrefix() + "cap_get_proc() failed: " + strerror(errno));
+            exit(1);
+        }
+
+        if (cap_set_flag(caps, CAP_EFFECTIVE, sizeof(cap_list)/sizeof(cap_list[0]), cap_list, CAP_CLEAR) == -1 ||
+            cap_set_flag(caps, CAP_PERMITTED, sizeof(cap_list)/sizeof(cap_list[0]), cap_list, CAP_CLEAR) == -1)
+        {
+            Application::instance().logger().error(Util::logPrefix() +  "cap_set_flag() failed: " + strerror(errno));
+            exit(1);
+        }
+
+        if (cap_set_proc(caps) == -1)
+        {
+            Application::instance().logger().error(std::string("cap_set_proc() failed: ") + strerror(errno));
+            exit(1);
+        }
+
+        char *capText = cap_to_text(caps, NULL);
+        Application::instance().logger().information(Util::logPrefix() + "Capabilities now: " + capText);
+        cap_free(capText);
+
+        cap_free(caps);
+#endif
+        // We assume that on non-Linux we don't need to be root to be able to hardlink to files we
+        // don't own, so drop root.
+        if (geteuid() == 0 && getuid() != 0)
+        {
+            // The program is setuid root. Not normal on Linux where we use setcap, but if this
+            // needs to run on non-Linux Unixes, setuid root is what it will bneed to be to be able
+            // to do chroot().
+            if (setuid(getuid()) != 0) {
+                Application::instance().logger().error(std::string("setuid() failed: ") + strerror(errno));
+            }
+        }
+#if ENABLE_DEBUG
+        if (geteuid() == 0 && getuid() == 0)
+        {
+#ifdef __linux
+            // Argh, awful hack
+            if (capability == CAP_FOWNER)
+                return;
+#endif
+
+            // Running under sudo, probably because being debugged? Let's drop super-user rights.
+            LOOLWSD::runningAsRoot = true;
+            if (LOOLWSD::uid == 0)
+            {
+                struct passwd *nobody = getpwnam("nobody");
+                if (nobody)
+                    LOOLWSD::uid = nobody->pw_uid;
+                else
+                    LOOLWSD::uid = 65534;
+            }
+            if (setuid(LOOLWSD::uid) != 0) {
+                Application::instance().logger().error(std::string("setuid() failed: ") + strerror(errno));
+            }
+        }
+#endif
+    }
+}
 
 class QueueHandler: public Runnable
 {
@@ -327,91 +406,6 @@ public:
     }
 };
 
-class TestOutput : public Runnable
-{
-public:
-    TestOutput(WebSocket& ws) :
-        _ws(ws)
-    {
-    }
-
-    void run() override
-    {
-        int flags;
-        int n;
-        _ws.setReceiveTimeout(0);
-        try
-        {
-            do
-            {
-                char buffer[100000];
-                n = _ws.receiveFrame(buffer, sizeof(buffer), flags);
-
-                if (n > 0 && (flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE)
-                {
-                    std::cout <<
-                        Util::logPrefix() <<
-                        "Client got " << n << " bytes: " << getAbbreviatedMessage(buffer, n) <<
-                        std::endl;
-                }
-            }
-            while (n > 0 && (flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE);
-        }
-        catch (WebSocketException& exc)
-        {
-            Application::instance().logger().error(Util::logPrefix() + "WebSocketException: " + exc.message());
-            _ws.close();
-        }
-    }
-
-private:
-    WebSocket& _ws;
-};
-
-class TestInput : public Runnable
-{
-public:
-    TestInput(ServerApplication& main, ServerSocket& svs, HTTPServer& srv) :
-        _main(main),
-        _svs(svs),
-        _srv(srv)
-    {
-    }
-
-    void run() override
-    {
-        HTTPClientSession cs("127.0.0.1", _svs.address().port());
-        HTTPRequest request(HTTPRequest::HTTP_GET, "/ws");
-        HTTPResponse response;
-        WebSocket ws(cs, request, response);
-
-        Thread thread;
-        TestOutput output(ws);
-        thread.start(output);
-
-        if (isatty(0))
-        {
-            std::cout << std::endl;
-            std::cout << "Enter LOOL WS requests, one per line. Enter EOF to finish." << std::endl;
-        }
-
-        while (!std::cin.eof())
-        {
-            std::string line;
-            std::getline(std::cin, line);
-            ws.sendFrame(line.c_str(), line.size());
-        }
-        thread.join();
-        _srv.stopAll();
-        _main.terminate();
-    }
-
-private:
-    ServerApplication& _main;
-    ServerSocket& _svs;
-    HTTPServer& _srv;
-};
-
 int LOOLWSD::portNumber = DEFAULT_CLIENT_PORT_NUMBER;
 std::string LOOLWSD::cache = LOOLWSD_CACHEDIR;
 std::string LOOLWSD::sysTemplate;
@@ -562,436 +556,35 @@ void LOOLWSD::displayHelp()
     helpFormatter.format(std::cout);
 }
 
-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
-#endif
-                        )
-    {
-#ifdef __linux
-        cap_t caps;
-        cap_value_t cap_list[] = { capability };
-
-        caps = cap_get_proc();
-        if (caps == NULL)
-        {
-            Application::instance().logger().error(Util::logPrefix() + "cap_get_proc() failed: " + strerror(errno));
-            exit(1);
-        }
-
-        if (cap_set_flag(caps, CAP_EFFECTIVE, sizeof(cap_list)/sizeof(cap_list[0]), cap_list, CAP_CLEAR) == -1 ||
-            cap_set_flag(caps, CAP_PERMITTED, sizeof(cap_list)/sizeof(cap_list[0]), cap_list, CAP_CLEAR) == -1)
-        {
-            Application::instance().logger().error(Util::logPrefix() +  "cap_set_flag() failed: " + strerror(errno));
-            exit(1);
-        }
-
-        if (cap_set_proc(caps) == -1)
-        {
-            Application::instance().logger().error(std::string("cap_set_proc() failed: ") + strerror(errno));
-            exit(1);
-        }
-
-        char *capText = cap_to_text(caps, NULL);
-        Application::instance().logger().information(Util::logPrefix() + "Capabilities now: " + capText);
-        cap_free(capText);
-
-        cap_free(caps);
-#endif
-        // We assume that on non-Linux we don't need to be root to be able to hardlink to files we
-        // don't own, so drop root.
-        if (geteuid() == 0 && getuid() != 0)
-        {
-            // The program is setuid root. Not normal on Linux where we use setcap, but if this
-            // needs to run on non-Linux Unixes, setuid root is what it will bneed to be to be able
-            // to do chroot().
-            if (setuid(getuid()) != 0) {
-                Application::instance().logger().error(std::string("setuid() failed: ") + strerror(errno));
-            }
-        }
-#if ENABLE_DEBUG
-        if (geteuid() == 0 && getuid() == 0)
-        {
-#ifdef __linux
-            // Argh, awful hack
-            if (capability == CAP_FOWNER)
-                return;
-#endif
-
-            // Running under sudo, probably because being debugged? Let's drop super-user rights.
-            LOOLWSD::runningAsRoot = true;
-            if (LOOLWSD::uid == 0)
-            {
-                struct passwd *nobody = getpwnam("nobody");
-                if (nobody)
-                    LOOLWSD::uid = nobody->pw_uid;
-                else
-                    LOOLWSD::uid = 65534;
-            }
-            if (setuid(LOOLWSD::uid) != 0) {
-                Application::instance().logger().error(std::string("setuid() failed: ") + strerror(errno));
-            }
-        }
-#endif
-    }
-}
-
-// Writer, Impress or Calc
-void LOOLWSD::componentMain()
-{
-    try
-    {
-        _namedMutexLOOL.lock();
-
-#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 (!loKit)
-        {
-            logger().fatal(Util::logPrefix() + "LibreOfficeKit initialisation failed");
-            exit(Application::EXIT_UNAVAILABLE);
-        }
-
-        _namedMutexLOOL.unlock();
-
-        // Open websocket connection between the child process and the
-        // parent. The parent forwards us requests that it can't handle.
-
-        HTTPClientSession cs("127.0.0.1", MASTER_PORT_NUMBER);
-        cs.setTimeout(0);
-        HTTPRequest request(HTTPRequest::HTTP_GET, LOOLWSD::CHILD_URI);
-        HTTPResponse response;
-        std::shared_ptr<WebSocket> ws(new WebSocket(cs, request, response));
-
-        std::shared_ptr<ChildProcessSession> session(new ChildProcessSession(ws, loKit));
-
-        ws->setReceiveTimeout(0);
-
-        std::string hello("child " + std::to_string(_childId));
-        session->sendTextFrame(hello);
-
-        tsqueue<std::string> queue;
-        Thread queueHandlerThread;
-        QueueHandler handler(queue);
-
-        handler.setSession(session);
-        queueHandlerThread.start(handler);
-
-        int flags;
-        int n;
-        do
-        {
-            char buffer[1024];
-            n = ws->receiveFrame(buffer, sizeof(buffer), flags);
-
-            if (n > 0 && (flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE)
-            {
-                std::string firstLine = getFirstLine(buffer, n);
-                StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
-
-                // The only kind of messages a child process receives are the single-line ones (?)
-                assert(firstLine.size() == static_cast<std::string::size_type>(n));
-
-                // Check if it is a "canceltiles" and in that case remove outstanding
-                // "tile" messages from the queue.
-                if (tokens.count() == 1 && tokens[0] == "canceltiles")
-                {
-                    queue.remove_if([](std::string& x) {
-                        return (x.find("tile ") == 0 && x.find("id=") == std::string::npos);
-                    });
-                }
-                else
-                {
-                    queue.put(firstLine);
-                }
-            }
-        }
-        while (n > 0 && (flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE);
-
-        queue.clear();
-        queue.put("eof");
-        queueHandlerThread.join();
-    }
-    catch (Exception& exc)
-    {
-        logger().log(Util::logPrefix() + "Exception: " + exc.what());
-    }
-    catch (std::exception& exc)
-    {
-        logger().error(Util::logPrefix() + "Exception: " + exc.what());
-    }
-
-    exit(Application::EXIT_OK);
-}
-
-int LOOLWSD::createComponent()
-{
-    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()
+int LOOLWSD::createBroker()
 {
-    // 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));
+    Process::Args args;
 
-    }
-    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
+    //args.push_back("--child=" + std::to_string(_childId));
+    //args.push_back("--port=" + std::to_string(LOOLWSD::portNumber));
+    //args.push_back("--jail=" + LOOLWSD::jail);
+    args.push_back("--losubpath=" + LOOLWSD::loSubPath);
+    args.push_back("--systemplate=" + sysTemplate);
+    args.push_back("--lotemplate=" + loTemplate);
+    args.push_back("--childroot=" + childRoot);
+    args.push_back("--numprespawns=" + std::to_string(_numPreSpawnedChildren));
 
-    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);
-    }
+    std::string executable = Path(Application::instance().commandPath()).parent().toString() + "loolbroker";
+    
+    Application::instance().logger().information(Util::logPrefix() + "Launching broker: " + executable + " " + Poco::cat(std::string(" "), args.begin(), args.end()));
+    
+    ProcessHandle child = Process::launch(executable, args);
 
-    if (chdir("/") == -1)
-    {
-        logger().error(std::string("chdir(\"/\") in jail failed: ") + strerror(errno));
-        exit(Application::EXIT_UNAVAILABLE);
-    }
-
-#ifdef __linux
-    dropCapability(CAP_SYS_CHROOT);
-#else
-    dropCapability();
-#endif
-
-    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);
-    }
-
-    startupComponent(_numPreSpawnedChildren);
-
-    while (MasterProcessSession::_childProcesses.size() > 0)
-    {
-        int status;
-        pid_t pid = waitpid(-1, &status, WUNTRACED | WNOHANG);
-        if (pid > 0)
-        {
-            if ( MasterProcessSession::_childProcesses.find(pid) != MasterProcessSession::_childProcesses.end() )
-            {
-                if ((WIFEXITED(status) || WIFSIGNALED(status) || WTERMSIG(status) ) )
-                {
-                    std::cout << Util::logPrefix() << "One of our known child processes died :" << std::to_string(pid)  << std::endl;
-                    MasterProcessSession::_childProcesses.erase(pid);
-                }
-
-                if ( WCOREDUMP(status) )
-                    std::cout << Util::logPrefix() << "The child produced a core dump." << std::endl;
-
-                if ( WIFSTOPPED(status) )
-                    std::cout << Util::logPrefix() << "The child process was stopped by delivery of a signal." << std::endl;
-
-                if ( WSTOPSIG(status) )

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list