[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