common/Util.cpp | 10 + common/Util.hpp | 3 loleaflet/html/loleaflet.html.m4 | 2 loleaflet/src/core/Socket.js | 9 - net/WebSocketHandler.hpp | 275 +++++++++++++++++++++++++-------------- test/test.cpp | 39 ++--- wsd/Admin.cpp | 7 wsd/ClientSession.cpp | 8 - wsd/DocumentBroker.cpp | 3 wsd/LOOLWSD.cpp | 15 +- wsd/LOOLWSD.hpp | 3 11 files changed, 248 insertions(+), 126 deletions(-)
New commits: commit 994669aedbe131e3386110210b6863988c74fa5c Author: Gabriel Masei <[email protected]> AuthorDate: Fri Mar 8 10:21:17 2019 +0200 Commit: Michael Meeks <[email protected]> CommitDate: Fri May 17 11:44:38 2019 +0100 Added support for defragmentation of incoming websocket fragmented messages and handled some protocol error cases Change-Id: I4d11a6527b6b131c65101fd53b71015529645f74 Reviewed-on: https://gerrit.libreoffice.org/68901 Reviewed-by: Michael Meeks <[email protected]> Tested-by: Michael Meeks <[email protected]> diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp index 3b65ee89a..b4c14ede6 100644 --- a/net/WebSocketHandler.hpp +++ b/net/WebSocketHandler.hpp @@ -37,6 +37,8 @@ private: std::atomic<bool> _shuttingDown; bool _isClient; bool _isMasking; + bool _inFragmentBlock; + bool _isManualDefrag; protected: struct WSFrameMask @@ -50,16 +52,29 @@ protected: public: /// Perform upgrade ourselves, or select a client web socket. - WebSocketHandler(bool isClient = false, bool isMasking = true) : + /// Parameters: + /// isClient: the instance should behave like a client (true) or like a server (false) + /// (from websocket perspective) + /// isMasking: a client should mask (true) or not (false) outgoing frames + /// isManualDefrag: the message handler should be called for every fragment of a message and + /// defragmentation should be handled inside message handler (true) or the message handler + /// should be called after all fragments of a message were received and the message + /// was defragmented (false). + WebSocketHandler(bool isClient = false, bool isMasking = true, bool isManualDefrag = false) : _lastPingSentTime(std::chrono::steady_clock::now()), _pingTimeUs(0), _shuttingDown(false), _isClient(isClient), - _isMasking(isClient && isMasking) + _isMasking(isClient && isMasking), + _inFragmentBlock(false), + _isManualDefrag(isManualDefrag) { } /// Upgrades itself to a websocket directly. + /// Parameters: + /// socket: the TCP socket which received the upgrade request + /// request: the HTTP upgrade request to WebSocket WebSocketHandler(const std::weak_ptr<StreamSocket>& socket, const Poco::Net::HTTPRequest& request) : _socket(socket), @@ -69,7 +84,9 @@ public: _pingTimeUs(0), _shuttingDown(false), _isClient(false), - _isMasking(false) + _isMasking(false), + _inFragmentBlock(false), + _isManualDefrag(false) { upgradeToWebSocket(request); } @@ -99,8 +116,8 @@ public: RESERVED_TLS_FAILURE = 1015 }; - /// Sends WS shutdown message to the peer. - void shutdown(const StatusCodes statusCode = StatusCodes::NORMAL_CLOSE, const std::string& statusMessage = "") + /// Sends WS Close frame to the peer. + void sendCloseFrame(const StatusCodes statusCode = StatusCodes::NORMAL_CLOSE, const std::string& statusMessage = "") { std::shared_ptr<StreamSocket> socket = _socket.lock(); if (socket == nullptr) @@ -126,7 +143,22 @@ public: #endif } - bool handleOneIncomingMessage(const std::shared_ptr<StreamSocket>& socket) + void shutdown(const StatusCodes statusCode = StatusCodes::NORMAL_CLOSE, const std::string& statusMessage = "") + { + if (!_shuttingDown) + sendCloseFrame(statusCode, statusMessage); + std::shared_ptr<StreamSocket> socket = _socket.lock(); + if (socket) + { + socket->closeConnection(); + socket->getInBuffer().clear(); + } + _wsPayload.clear(); + _inFragmentBlock = false; + _shuttingDown = false; + } + + bool handleTCPStream(const std::shared_ptr<StreamSocket>& socket) { assert(socket && "Expected a valid socket instance."); @@ -177,7 +209,7 @@ public: headerLen += 8; } - unsigned char *data, *mask; + unsigned char *data, *mask = nullptr; if (hasMask) { @@ -187,121 +219,164 @@ public: if (payloadLen + headerLen > len) { // partial read wait for more data. - LOG_TRC("#" << socket->getFD() << ": Still incomplete WebSocket message, have " << len - << " bytes, message is " << payloadLen + headerLen << " bytes"); + LOG_TRC("#" << socket->getFD() << ": Still incomplete WebSocket frame, have " << len << " bytes, frame is " << payloadLen + headerLen << " bytes"); return false; } - LOG_TRC("#" << socket->getFD() << ": Incoming WebSocket data of " << len << " bytes: " - << Util::stringifyHexLine(socket->getInBuffer(), 0, std::min((size_t)32, len))); - - data = p + headerLen; - - if (hasMask) + if (hasMask && _isClient) { - const size_t end = _wsPayload.size(); - _wsPayload.resize(end + payloadLen); - char* wsData = &_wsPayload[end]; - for (size_t i = 0; i < payloadLen; ++i) - *wsData++ = data[i] ^ mask[i % 4]; - } else - _wsPayload.insert(_wsPayload.end(), data, data + payloadLen); -#else - unsigned char * const p = reinterpret_cast<unsigned char*>(&socket->getInBuffer()[0]); - _wsPayload.insert(_wsPayload.end(), p, p + len); - const size_t headerLen = 0; - const size_t payloadLen = len; -#endif - - assert(_wsPayload.size() >= payloadLen); + LOG_ERR("#" << socket->getFD() << ": Servers should not send masked frames. Only clients."); + shutdown(StatusCodes::PROTOCOL_ERROR); + return true; + } - socket->getInBuffer().erase(socket->getInBuffer().begin(), - socket->getInBuffer().begin() + headerLen + payloadLen); + LOG_TRC("#" << socket->getFD() << ": Incoming WebSocket data of " << len << " bytes: " << Util::stringifyHexLine(socket->getInBuffer(), 0, std::min((size_t)32, len))); -#if !MOBILEAPP + data = p + headerLen; - // FIXME: fin, aggregating payloads into _wsPayload etc. - LOG_TRC("#" << socket->getFD() << ": Incoming WebSocket message code " - << static_cast<unsigned>(code) << ", fin? " << fin << ", mask? " << hasMask - << ", payload length: " << _wsPayload.size() - << ", residual socket data: " << socket->getInBuffer().size() << " bytes."); + if (isControlFrame(code)) + { + //Process control frames + std::vector<char> ctrlPayload; - bool doClose = false; + readPayload(data, payloadLen, mask, ctrlPayload); + socket->getInBuffer().erase(socket->getInBuffer().begin(), socket->getInBuffer().begin() + headerLen + payloadLen); + LOG_TRC("#" << socket->getFD() << ": Incoming WebSocket frame code " << static_cast<unsigned>(code) << + ", fin? " << fin << ", mask? " << hasMask << ", payload length: " << payloadLen << + ", residual socket data: " << socket->getInBuffer().size() << " bytes."); - switch (code) - { - case WSOpCode::Pong: - { - if (_isClient) + // All control frames MUST NOT be fragmented and MUST have a payload length of 125 bytes or less + if (!fin) { - LOG_ERR("#" << socket->getFD() << ": Servers should not send pongs, only clients"); - doClose = true; - break; + LOG_ERR("#" << socket->getFD() << ": A control frame cannot be fragmented."); + shutdown(StatusCodes::PROTOCOL_ERROR); + return true; } - else + if (payloadLen > 125) { - _pingTimeUs = std::chrono::duration_cast<std::chrono::microseconds> - (std::chrono::steady_clock::now() - _lastPingSentTime).count(); - LOG_TRC("#" << socket->getFD() << ": Pong received: " << _pingTimeUs << " microseconds"); - break; + LOG_ERR("#" << socket->getFD() << ": The payload length of a control frame must not exceed 125 bytes."); + shutdown(StatusCodes::PROTOCOL_ERROR); + return true; } - } - case WSOpCode::Ping: - if (_isClient) + + switch (code) { - auto now = std::chrono::steady_clock::now(); - _pingTimeUs = std::chrono::duration_cast<std::chrono::microseconds> - (now - _lastPingSentTime).count(); - sendPong(now, &_wsPayload[0], payloadLen, socket); + case WSOpCode::Pong: + if (_isClient) + { + LOG_ERR("#" << socket->getFD() << ": Servers should not send pongs, only clients"); + shutdown(StatusCodes::POLICY_VIOLATION); + return true; + } + else + { + _pingTimeUs = std::chrono::duration_cast<std::chrono::microseconds> + (std::chrono::steady_clock::now() - _lastPingSentTime).count(); + LOG_TRC("#" << socket->getFD() << ": Pong received: " << _pingTimeUs << " microseconds"); + } + break; + case WSOpCode::Ping: + if (_isClient) + { + auto now = std::chrono::steady_clock::now(); + _pingTimeUs = std::chrono::duration_cast<std::chrono::microseconds> + (now - _lastPingSentTime).count(); + sendPong(now, &ctrlPayload[0], payloadLen, socket); + } + else + { + LOG_ERR("#" << socket->getFD() << ": Clients should not send pings, only servers"); + shutdown(StatusCodes::POLICY_VIOLATION); + return true; + } + break; + case WSOpCode::Close: + { + std::string message; + StatusCodes statusCode = StatusCodes::NORMAL_CLOSE; + if (!_shuttingDown) + { + // Peer-initiated shutdown must be echoed. + // Otherwise, this is the echo to _our_ shutdown message, which we should ignore. + LOG_TRC("#" << socket->getFD() << ": Peer initiated socket shutdown. Code: " << static_cast<int>(statusCode)); + if (ctrlPayload.size()) + { + statusCode = static_cast<StatusCodes>((((uint64_t)(unsigned char)ctrlPayload[0]) << 8) + + (((uint64_t)(unsigned char)ctrlPayload[1]) << 0)); + if (ctrlPayload.size() > 2) + message.assign(&ctrlPayload[2], &ctrlPayload[2] + ctrlPayload.size() - 2); + } + } + shutdown(statusCode, message); + return true; + } + default: + LOG_ERR("#" << socket->getFD() << ": Received unknown control code"); + shutdown(StatusCodes::PROTOCOL_ERROR); break; } - else + + return true; + } + + // Check data frames for errors + if (_inFragmentBlock) + { + if (code != WSOpCode::Continuation) { - LOG_ERR("#" << socket->getFD() << ": Clients should not send pings, only servers"); - doClose = true; + LOG_ERR("#" << socket->getFD() << ": A fragment that is not the first fragment of a message must have the opcode equal to 0."); + shutdown(StatusCodes::PROTOCOL_ERROR); + return true; } - break; - case WSOpCode::Close: - doClose = true; - break; - default: - handleMessage(fin, code, _wsPayload); - break; + } + else if (code == WSOpCode::Continuation) + { + LOG_ERR("#" << socket->getFD() << ": An unfragmented message or the first fragment of a fragmented message must have the opcode different than 0."); + shutdown(StatusCodes::PROTOCOL_ERROR); + return true; } + //Process data frame + readPayload(data, payloadLen, mask, _wsPayload); #else - handleMessage(true, WSOpCode::Binary, _wsPayload); - + unsigned char * const p = reinterpret_cast<unsigned char*>(&socket->getInBuffer()[0]); + _wsPayload.insert(_wsPayload.end(), p, p + len); + const size_t headerLen = 0; + const size_t payloadLen = len; #endif + socket->getInBuffer().erase(socket->getInBuffer().begin(), socket->getInBuffer().begin() + headerLen + payloadLen); + #if !MOBILEAPP - if (doClose) + + LOG_TRC("#" << socket->getFD() << ": Incoming WebSocket frame code " << static_cast<unsigned>(code) << + ", fin? " << fin << ", mask? " << hasMask << ", payload length: " << payloadLen << + ", residual socket data: " << socket->getInBuffer().size() << " bytes."); + + if (fin) + { + //If is final fragment then process the accumulated message. + handleMessage(fin, code, _wsPayload); + _inFragmentBlock = false; + } + else { - if (!_shuttingDown) + if (_isManualDefrag) { - // Peer-initiated shutdown must be echoed. - // Otherwise, this is the echo to _our_ shutdown message, which we should ignore. - const StatusCodes statusCode = static_cast<StatusCodes>((((uint64_t)(unsigned char)_wsPayload[0]) << 8) + - (((uint64_t)(unsigned char)_wsPayload[1]) << 0)); - LOG_TRC("#" << socket->getFD() << ": Client initiated socket shutdown. Code: " << static_cast<int>(statusCode)); - if (_wsPayload.size() > 2) - { - const std::string message(&_wsPayload[2], &_wsPayload[2] + _wsPayload.size() - 2); - shutdown(statusCode, message); - } - else - { - shutdown(statusCode); - } + //If the user wants to process defragmentation on its own then let him process it. + handleMessage(fin, code, _wsPayload); + _inFragmentBlock = true; } else { - LOG_TRC("#" << socket->getFD() << ": Client responded to our shutdown."); + _inFragmentBlock = true; + //If is not final fragment then wait for next fragment. + return false; } - - // TCP Close. - socket->closeConnection(); } +#else + handleMessage(true, WSOpCode::Binary, _wsPayload); + #endif _wsPayload.clear(); @@ -332,7 +407,7 @@ public: #endif else { - while (handleOneIncomingMessage(socket)) + while (handleTCPStream(socket)) ; // might have multiple messages in the accumulated buffer. } } @@ -516,6 +591,22 @@ private: protected: + bool isControlFrame(WSOpCode code){ return code >= WSOpCode::Close; } + + void readPayload(unsigned char *data, size_t dataLen, unsigned char* mask, std::vector<char>& payload) + { + if (mask) + { + size_t end = payload.size(); + payload.resize(end + dataLen); + char* wsData = &payload[end]; + for (size_t i = 0; i < dataLen; ++i) + *wsData++ = data[i] ^ mask[i % 4]; + } + else + payload.insert(payload.end(), data, data + dataLen); + } + /// To be overriden to handle the websocket messages the way you need. virtual void handleMessage(bool /*fin*/, WSOpCode /*code*/, std::vector<char> &/*data*/) { commit 204de0e1120360af0451dcac83159cc8490073e2 Author: Michael Meeks <[email protected]> AuthorDate: Thu May 16 21:12:20 2019 +0100 Commit: Michael Meeks <[email protected]> CommitDate: Thu May 16 21:12:20 2019 +0100 debug: show WebSocketURI & a unique host id in help -> about. Rather useful for debugging clustering issues. Change-Id: I6d5f224bf8a3e4034c419137c8ad2b17fdf265ed diff --git a/common/Util.cpp b/common/Util.cpp index b0af5c6c6..6f2322c8f 100644 --- a/common/Util.cpp +++ b/common/Util.cpp @@ -44,6 +44,7 @@ #include <thread> #include <Poco/Base64Encoder.h> +#include <Poco/HexBinaryEncoder.h> #include <Poco/ConsoleChannel.h> #include <Poco/Exception.h> #include <Poco/Format.h> @@ -100,6 +101,15 @@ namespace Util return v; } + /// Generate a string of random characters. + std::string getHexString(const size_t length) + { + std::stringstream ss; + Poco::HexBinaryEncoder hex(ss); + hex.write(getBytes(length).data(), length); + return ss.str().substr(0, length); + } + /// Generates a random string in Base64. /// Note: May contain '/' characters. std::string getB64String(const size_t length) diff --git a/common/Util.hpp b/common/Util.hpp index a92f0c0cc..96e61d4e9 100644 --- a/common/Util.hpp +++ b/common/Util.hpp @@ -46,6 +46,9 @@ namespace Util /// Generate an array of random characters. std::vector<char> getBytes(const size_t length); + /// Generate a string of random characters. + std::string getHexString(const size_t length); + /// Generates a random string suitable for /// file/directory names. std::string getFilename(const size_t length); diff --git a/loleaflet/html/loleaflet.html.m4 b/loleaflet/html/loleaflet.html.m4 index 367ed3fe6..1a44c00b6 100644 --- a/loleaflet/html/loleaflet.html.m4 +++ b/loleaflet/html/loleaflet.html.m4 @@ -156,6 +156,8 @@ ifelse(MOBILEAPP,[true], <div id="loolwsd-version"></div> <h3>LOKit</h3> <div id="lokit-version"></div> + <h3>Id</h3> + <div id="loolwsd-id"></div> <p>Copyright _YEAR_, Collabora Productivity Limited.</p> </div> diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js index 275ea8720..c57d0b691 100644 --- a/loleaflet/src/core/Socket.js +++ b/loleaflet/src/core/Socket.js @@ -50,6 +50,10 @@ L.Socket = L.Class.extend({ this._msgQueue = []; }, + getWebSocketBaseURI: function(map) { + return map.options.server + map.options.serviceRoot + '/lool/' + encodeURIComponent(map.options.doc + '?' + $.param(map.options.docParams)) + '/ws'; + }, + connect: function(socket) { var map = this._map; if (map.options.permission) { @@ -70,8 +74,7 @@ L.Socket = L.Class.extend({ } try { - var websocketURI = map.options.server + map.options.serviceRoot + '/lool/' + encodeURIComponent(map.options.doc + '?' + $.param(map.options.docParams)) + '/ws' + wopiSrc; - this.socket = new WebSocket(websocketURI); + this.socket = new WebSocket(this.getWebSocketBaseURI(map) + wopiSrc); } catch (e) { // On IE 11 there is a limitation on the number of WebSockets open to a single domain (6 by default and can go to 128). // Detect this and hint the user. @@ -284,6 +287,8 @@ L.Socket = L.Class.extend({ $('#loolwsd-version').text(loolwsdVersionObj.Version); } + $('#loolwsd-id').html('<p>' + this.getWebSocketBaseURI(this._map) + '</p><p>' + loolwsdVersionObj.Id + '</p>'); + // TODO: For now we expect perfect match in protocol versions if (loolwsdVersionObj.Protocol !== this.ProtocolVersionNumber) { this._map.fire('error', {msg: _('Unsupported server version.')}); diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp index 414427027..6dd699b09 100644 --- a/wsd/Admin.cpp +++ b/wsd/Admin.cpp @@ -122,12 +122,7 @@ void AdminSocketHandler::handleMessage(bool /* fin */, WSOpCode /* code */, else if (tokens[0] == "version") { // Send LOOL version information - std::string version, hash; - Util::getVersionInfo(version, hash); - std::string versionStr = - "{ \"Version\": \"" + version + "\", " + - "\"Hash\": \"" + hash + "\" }"; - sendTextFrame("loolserver " + versionStr); + sendTextFrame("loolserver " + LOOLWSD::getVersionJSON()); // Send LOKit version information sendTextFrame("lokitversion " + LOOLWSD::LOKitVersion); } diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp index 3ba3580e7..f1ae9e897 100644 --- a/wsd/ClientSession.cpp +++ b/wsd/ClientSession.cpp @@ -112,13 +112,7 @@ bool ClientSession::_handleInput(const char *buffer, int length) } // Send LOOL version information - std::string version, hash; - Util::getVersionInfo(version, hash); - std::string versionStr = - "{ \"Version\": \"" + version + "\", " + - "\"Hash\": \"" + hash + "\", " + - "\"Protocol\": \"" + GetProtocolVersion() + "\" }"; - sendTextFrame("loolserver " + versionStr); + sendTextFrame("loolserver " + LOOLWSD::getVersionJSON()); // Send LOKit version information sendTextFrame("lokitversion " + LOOLWSD::LOKitVersion); diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index f355e6b7b..9bdd7d434 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -686,6 +686,7 @@ std::string LOOLWSD::ServerName; std::string LOOLWSD::FileServerRoot; std::string LOOLWSD::ServiceRoot; std::string LOOLWSD::LOKitVersion; +std::string LOOLWSD::HostIdentifier; std::string LOOLWSD::ConfigFile = LOOLWSD_CONFIGDIR "/loolwsd.xml"; std::string LOOLWSD::ConfigDir = LOOLWSD_CONFIGDIR "/conf.d"; std::string LOOLWSD::LogLevel = "trace"; @@ -3085,6 +3086,17 @@ private: } }; +std::string LOOLWSD::getVersionJSON() +{ + std::string version, hash; + Util::getVersionInfo(version, hash); + return + "{ \"Version\": \"" + version + "\", " + "\"Hash\": \"" + hash + "\", " + "\"Protocol\": \"" + GetProtocolVersion() + "\", " + "\"Id\": \"" + HostIdentifier + "\" }"; +} + static LOOLWSDServer srv; int LOOLWSD::innerMain() @@ -3100,11 +3112,12 @@ int LOOLWSD::innerMain() Environment::set("LD_BIND_NOW", "1"); #if !MOBILEAPP + HostIdentifier = Util::rng::getHexString(8); if (DisplayVersion) { std::string version, hash; Util::getVersionInfo(version, hash); - LOG_INF("Loolwsd version details: " << version << " - " << hash); + LOG_INF("Loolwsd version details: " << version << " - " << hash << " - id " << HostIdentifier); } #endif #endif diff --git a/wsd/LOOLWSD.hpp b/wsd/LOOLWSD.hpp index ce5f8d172..7cb03920f 100644 --- a/wsd/LOOLWSD.hpp +++ b/wsd/LOOLWSD.hpp @@ -63,6 +63,7 @@ public: static std::string FileServerRoot; static std::string ServiceRoot; ///< There are installations that need prefixing every page with some path. static std::string LOKitVersion; + static std::string HostIdentifier; ///< A unique random hash that identifies this server static std::string LogLevel; static bool AnonymizeUserData; static std::uint64_t AnonymizationSalt; @@ -152,6 +153,8 @@ public: return AnonymizeUserData ? Util::anonymize(username, AnonymizationSalt) : username; } + static std::string getVersionJSON(); + int innerMain(); protected: commit b108a8997a01099a4120c6c5c3f01c61b086cebf Author: Michael Meeks <[email protected]> AuthorDate: Thu May 16 20:38:36 2019 +0100 Commit: Michael Meeks <[email protected]> CommitDate: Thu May 16 20:38:36 2019 +0100 debug: dump content of paste messages. Problematic not to be able to see the content, helps hunt weirdness in eg. pasted RTF. Change-Id: I301bfe040a2424b6ca84ab94b8eee865439fb680 diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp index 744d4ec87..d33337cc1 100644 --- a/wsd/DocumentBroker.cpp +++ b/wsd/DocumentBroker.cpp @@ -1738,6 +1738,9 @@ bool DocumentBroker::forwardToChild(const std::string& viewId, const std::string LOG_TRC("Forwarding payload to child [" << viewId << "]: " << getAbbreviatedMessage(message)); + if (Log::traceEnabled() && Util::startsWith(message, "paste ")) + LOG_TRC("Logging paste payload (" << message.size() << " bytes) '" << message << "' end paste"); + std::string msg = "child-" + viewId + ' ' + message; const auto it = _sessions.find(viewId); commit 1fb87c20a3ddb672c113524fb8e15dfdf47d344c Author: Michael Meeks <[email protected]> AuthorDate: Mon May 13 15:39:14 2019 +0100 Commit: Michael Meeks <[email protected]> CommitDate: Thu May 16 14:49:41 2019 +0100 test: use process groups as well to allow concurrent make checks. Change-Id: Ib1a55f53c51835a8f9fb1c17146f30e887103906 diff --git a/test/test.cpp b/test/test.cpp index 9745dda00..4f90ee1b5 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -164,11 +164,14 @@ bool runClientTests(bool standalone, bool verbose) // Versions assuming a single user, on a single machine #ifndef UNIT_CLIENT_TESTS -std::vector<int> getProcPids(const char* exec_filename, bool ignoreZombies = true) +std::vector<int> getProcPids(const char* exec_filename) { std::vector<int> pids; - // Crash all lokit processes. + // Ensure we're in the same group. + int grp = getpgrp(); + + // Get all lokit processes. for (auto it = Poco::DirectoryIterator(std::string("/proc")); it != Poco::DirectoryIterator(); ++it) { try @@ -192,24 +195,24 @@ std::vector<int> getProcPids(const char* exec_filename, bool ignoreZombies = tru std::string statString; Poco::StreamCopier::copyToString(stat, statString); Poco::StringTokenizer tokens(statString, " "); - if (tokens.count() > 3 && tokens[1] == exec_filename) + if (tokens.count() > 6 && tokens[1] == exec_filename) { - if (ignoreZombies) + // We could have several make checks running at once. + int kidGrp = std::atoi(tokens[4].c_str()); + if (kidGrp != grp) + continue; + + switch (tokens[2].c_str()[0]) { - switch (tokens[2].c_str()[0]) - { - // Dead & zombie markers for old and new kernels. - case 'x': - case 'X': - case 'Z': - break; - default: - pids.push_back(pid); - break; - } - } - else + // Dead & zombie markers for old and new kernels. + case 'x': + case 'X': + case 'Z': + break; + default: pids.push_back(pid); + break; + } } } } @@ -231,7 +234,7 @@ std::vector<int> getKitPids() int getLoolKitProcessCount() { - return getProcPids("(loolkit)", true).size(); + return getProcPids("(loolkit)").size(); } std::vector<int> getForKitPids() _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
