2015-04-13 04:09:02 -05:00
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2015-03-04 17:14:04 -06:00
/*
* 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/.
*
* Parts of this file is covered by :
Boost Software License - Version 1.0 - August 17 th , 2003
Permission is hereby granted , free of charge , to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license ( the " Software " ) to use , reproduce , display , distribute ,
execute , and transmit the Software , and to prepare derivative works of the
Software , and to permit third - parties to whom the Software is furnished to
do so , all subject to the following :
The copyright notices in the Software and this entire statement , including
the above license grant , this restriction and the following disclaimer ,
must be included in all copies of the Software , in whole or in part , and
all derivative works of the Software , unless such copies or derivative
works are solely in the form of machine - executable object code generated by
a source language processor .
THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE , TITLE AND NON - INFRINGEMENT . IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY , WHETHER IN CONTRACT , TORT OR OTHERWISE ,
ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE .
*/
2015-04-21 08:51:28 -05:00
# include "config.h"
2015-04-10 07:20:04 -05:00
// This is the main source for the loolwsd program. LOOL uses several loolwsd processes: one main
// parent process that listens on the TCP port and accepts connections from LOOL clients, and a
// number of child processes, each which handles a viewing (editing) session for one document.
2015-04-16 11:15:40 -05:00
# include <errno.h>
2015-10-13 12:05:42 -05:00
# include <locale.h>
2015-04-08 09:22:42 -05:00
# include <unistd.h>
2015-04-16 11:15:40 -05:00
# include <sys/types.h>
# include <sys/wait.h>
2015-08-08 06:55:54 -05:00
# include <sys/prctl.h>
2015-04-16 11:15:40 -05:00
2015-07-13 09:13:06 -05:00
# include <ftw.h>
2016-03-22 13:27:38 -05:00
# include <time.h>
2015-07-13 09:13:06 -05:00
# include <utime.h>
2015-04-16 11:15:40 -05:00
# include <cassert>
2015-03-28 06:55:35 -05:00
# include <cstdlib>
2015-04-14 09:50:38 -05:00
# include <cstring>
2015-03-04 17:14:04 -06:00
# include <iostream>
2016-03-22 13:27:38 -05:00
# include <map>
2015-07-13 09:13:06 -05:00
# include <mutex>
2016-03-08 01:31:29 -06:00
# include <sstream>
2016-03-22 13:27:38 -05:00
# include <unordered_set>
2015-03-04 17:14:04 -06:00
2016-03-22 10:25:35 -05:00
# include <Poco/DOM/AutoPtr.h>
# include <Poco/DOM/DOMParser.h>
# include <Poco/DOM/DOMWriter.h>
# include <Poco/DOM/Document.h>
# include <Poco/DOM/Element.h>
# include <Poco/DOM/NodeList.h>
2015-05-08 07:46:10 -05:00
# include <Poco/Exception.h>
2015-04-16 11:15:40 -05:00
# include <Poco/File.h>
2016-02-24 01:39:23 -06:00
# include <Poco/FileStream.h>
2016-03-30 10:57:17 -05:00
# include <Poco/Net/AcceptCertificateHandler.h>
2016-03-21 03:37:39 -05:00
# include <Poco/Net/ConsoleCertificateHandler.h>
# include <Poco/Net/Context.h>
2015-10-16 10:38:24 -05:00
# include <Poco/Net/HTMLForm.h>
2015-03-04 17:14:04 -06:00
# include <Poco/Net/HTTPClientSession.h>
# include <Poco/Net/HTTPRequest.h>
# include <Poco/Net/HTTPRequestHandler.h>
# include <Poco/Net/HTTPRequestHandlerFactory.h>
# include <Poco/Net/HTTPServer.h>
# include <Poco/Net/HTTPServerParams.h>
# include <Poco/Net/HTTPServerParams.h>
# include <Poco/Net/HTTPServerRequest.h>
# include <Poco/Net/HTTPServerResponse.h>
2016-03-21 03:37:39 -05:00
# include <Poco/Net/InvalidCertificateHandler.h>
# include <Poco/Net/KeyConsoleHandler.h>
2015-10-16 10:38:24 -05:00
# include <Poco/Net/MessageHeader.h>
2016-02-24 01:39:23 -06:00
# include <Poco/Net/Net.h>
2015-03-04 17:14:04 -06:00
# include <Poco/Net/NetException.h>
2015-10-16 10:38:24 -05:00
# include <Poco/Net/PartHandler.h>
2016-03-21 03:37:39 -05:00
# include <Poco/Net/PrivateKeyPassphraseHandler.h>
2016-03-22 10:25:35 -05:00
# include <Poco/Net/SSLManager.h>
2016-03-21 03:37:39 -05:00
# include <Poco/Net/SecureServerSocket.h>
2015-03-04 17:14:04 -06:00
# include <Poco/Net/ServerSocket.h>
2015-05-08 13:24:46 -05:00
# include <Poco/Net/SocketAddress.h>
2015-03-04 17:14:04 -06:00
# include <Poco/Net/WebSocket.h>
2015-04-16 11:15:40 -05:00
# include <Poco/Path.h>
2015-03-27 11:23:27 -05:00
# include <Poco/Process.h>
2016-03-22 10:25:35 -05:00
# include <Poco/SAX/InputSource.h>
2016-02-24 01:39:23 -06:00
# include <Poco/StreamCopier.h>
2015-05-18 03:21:30 -05:00
# include <Poco/StringTokenizer.h>
2016-02-24 01:39:23 -06:00
# include <Poco/TemporaryFile.h>
2015-05-29 00:49:49 -05:00
# include <Poco/ThreadPool.h>
2016-02-24 01:39:23 -06:00
# include <Poco/URI.h>
2015-03-04 17:14:04 -06:00
# include <Poco/Util/HelpFormatter.h>
# include <Poco/Util/Option.h>
2015-04-08 09:22:42 -05:00
# include <Poco/Util/OptionException.h>
2015-03-04 17:14:04 -06:00
# include <Poco/Util/OptionSet.h>
# include <Poco/Util/ServerApplication.h>
2016-02-15 17:05:24 -06:00
# include "Admin.hpp"
# include "Auth.hpp"
2016-02-24 01:39:23 -06:00
# include "ChildProcessSession.hpp"
# include "Common.hpp"
2016-03-20 05:59:32 -05:00
# include "FileServer.hpp"
2015-04-14 09:50:38 -05:00
# include "LOOLProtocol.hpp"
2015-03-09 03:01:30 -05:00
# include "LOOLSession.hpp"
2015-03-17 18:56:15 -05:00
# include "LOOLWSD.hpp"
2016-02-24 01:39:23 -06:00
# include "MasterProcessSession.hpp"
2016-01-06 07:38:21 -06:00
# include "QueueHandler.hpp"
2016-03-25 21:56:18 -05:00
# include "Storage.hpp"
2016-03-27 14:22:24 -05:00
# include "IoUtil.hpp"
2015-03-17 18:56:15 -05:00
# include "Util.hpp"
2015-03-04 17:14:04 -06:00
2015-04-14 09:50:38 -05:00
using namespace LOOLProtocol ;
2015-05-29 00:49:49 -05:00
using Poco : : Exception ;
2015-04-16 11:15:40 -05:00
using Poco : : File ;
2016-02-24 01:39:23 -06:00
using Poco : : FileOutputStream ;
2015-05-29 00:49:49 -05:00
using Poco : : IOException ;
2016-02-24 01:39:23 -06:00
using Poco : : Net : : HTMLForm ;
2015-03-04 17:14:04 -06:00
using Poco : : Net : : HTTPClientSession ;
using Poco : : Net : : HTTPRequest ;
using Poco : : Net : : HTTPRequestHandler ;
using Poco : : Net : : HTTPRequestHandlerFactory ;
using Poco : : Net : : HTTPResponse ;
using Poco : : Net : : HTTPServer ;
using Poco : : Net : : HTTPServerParams ;
using Poco : : Net : : HTTPServerRequest ;
using Poco : : Net : : HTTPServerResponse ;
2016-02-24 01:39:23 -06:00
using Poco : : Net : : MessageHeader ;
using Poco : : Net : : NameValueCollection ;
using Poco : : Net : : PartHandler ;
2016-03-21 03:37:39 -05:00
using Poco : : Net : : SecureServerSocket ;
2015-03-04 17:14:04 -06:00
using Poco : : Net : : ServerSocket ;
2016-02-24 01:39:23 -06:00
using Poco : : Net : : Socket ;
2015-05-08 13:24:46 -05:00
using Poco : : Net : : SocketAddress ;
2015-03-04 17:14:04 -06:00
using Poco : : Net : : WebSocket ;
using Poco : : Net : : WebSocketException ;
2015-04-16 11:15:40 -05:00
using Poco : : Path ;
using Poco : : Process ;
2016-02-24 01:39:23 -06:00
using Poco : : ProcessHandle ;
2015-03-16 06:59:40 -05:00
using Poco : : Runnable ;
2016-02-24 01:39:23 -06:00
using Poco : : StreamCopier ;
2015-05-18 03:21:30 -05:00
using Poco : : StringTokenizer ;
2016-02-24 01:39:23 -06:00
using Poco : : TemporaryFile ;
2015-03-16 06:59:40 -05:00
using Poco : : Thread ;
2015-05-29 00:49:49 -05:00
using Poco : : ThreadPool ;
2016-02-24 01:39:23 -06:00
using Poco : : URI ;
2015-03-04 17:14:04 -06:00
using Poco : : Util : : Application ;
using Poco : : Util : : HelpFormatter ;
2015-04-08 09:22:42 -05:00
using Poco : : Util : : IncompatibleOptionsException ;
using Poco : : Util : : MissingOptionException ;
2015-03-04 17:14:04 -06:00
using Poco : : Util : : Option ;
using Poco : : Util : : OptionSet ;
using Poco : : Util : : ServerApplication ;
2016-03-15 10:35:59 -05:00
using Poco : : XML : : AutoPtr ;
using Poco : : XML : : DOMParser ;
using Poco : : XML : : DOMWriter ;
2016-03-22 10:25:35 -05:00
using Poco : : XML : : Element ;
using Poco : : XML : : InputSource ;
2016-03-15 10:35:59 -05:00
using Poco : : XML : : Node ;
using Poco : : XML : : NodeList ;
2015-03-04 17:14:04 -06:00
2016-04-03 09:33:35 -05:00
/// New LOK child processes ready to host documents.
static std : : vector < std : : shared_ptr < ChildProcess > > newChilds ;
static std : : mutex newChildsMutex ;
2016-03-22 11:47:05 -05:00
static std : : map < std : : string , std : : shared_ptr < DocumentBroker > > docBrokers ;
static std : : mutex docBrokersMutex ;
2016-01-06 23:16:47 -06:00
2016-04-03 18:00:37 -05:00
std : : shared_ptr < ChildProcess > getNewChild ( )
{
std : : unique_lock < std : : mutex > lock ( newChildsMutex ) ;
const signed available = newChilds . size ( ) ;
signed balance = LOOLWSD : : NumPreSpawnedChildren ;
if ( available = = 0 )
{
Log : : error ( " No available child. Sending spawn request to Broker and failing. " ) ;
}
else
{
balance - = available - 1 ;
}
const std : : string aMessage = " spawn " + std : : to_string ( balance ) + " \n " ;
Log : : debug ( " MasterToBroker: " + aMessage . substr ( 0 , aMessage . length ( ) - 1 ) ) ;
IoUtil : : writeFIFO ( LOOLWSD : : BrokerWritePipe , aMessage ) ;
if ( available > 0 )
{
auto child = newChilds . back ( ) ;
newChilds . pop_back ( ) ;
return child ;
}
return nullptr ;
}
2015-10-16 10:38:24 -05:00
/// Handles the filename part of the convert-to POST request payload.
2016-02-24 01:39:23 -06:00
class ConvertToPartHandler : public PartHandler
2015-10-16 10:38:24 -05:00
{
2015-10-19 09:03:16 -05:00
std : : string & _filename ;
2015-10-16 10:38:24 -05:00
public :
2015-10-20 08:00:05 -05:00
ConvertToPartHandler ( std : : string & filename )
: _filename ( filename )
2015-10-16 10:38:24 -05:00
{
}
2016-02-24 01:39:23 -06:00
virtual void handlePart ( const MessageHeader & header , std : : istream & stream ) override
2015-10-16 10:38:24 -05:00
{
2015-10-20 08:00:05 -05:00
// Extract filename and put it to a temporary directory.
2015-10-19 09:03:16 -05:00
std : : string disp ;
2016-02-24 01:39:23 -06:00
NameValueCollection params ;
2015-10-19 09:03:16 -05:00
if ( header . has ( " Content-Disposition " ) )
{
std : : string cd = header . get ( " Content-Disposition " ) ;
2016-02-24 01:39:23 -06:00
MessageHeader : : splitParameters ( cd , disp , params ) ;
2015-10-19 09:03:16 -05:00
}
2015-10-22 10:27:29 -05:00
2015-10-20 08:00:05 -05:00
if ( ! params . has ( " filename " ) )
return ;
2015-10-19 09:03:16 -05:00
2016-02-24 01:39:23 -06:00
Path tempPath = Path : : forDirectory ( TemporaryFile ( ) . tempName ( ) + Path : : separator ( ) ) ;
2015-10-20 08:00:05 -05:00
File ( tempPath ) . createDirectories ( ) ;
tempPath . setFileName ( params . get ( " filename " ) ) ;
_filename = tempPath . toString ( ) ;
// Copy the stream to _filename.
std : : ofstream fileStream ;
fileStream . open ( _filename ) ;
2016-02-24 01:39:23 -06:00
StreamCopier : : copyStream ( stream , fileStream ) ;
2015-10-20 08:00:05 -05:00
fileStream . close ( ) ;
2015-10-16 10:38:24 -05:00
}
} ;
2016-01-05 20:29:12 -06:00
/// Handle a public connection from a client.
class ClientRequestHandler : public HTTPRequestHandler
{
2016-01-24 13:48:09 -06:00
private :
2015-03-04 17:14:04 -06:00
2016-01-24 13:48:09 -06:00
void handlePostRequest ( HTTPServerRequest & request , HTTPServerResponse & response , const std : : string & id )
2015-03-04 17:14:04 -06:00
{
2016-03-15 10:35:59 -05:00
Log : : info ( " Post request: [ " + request . getURI ( ) + " ] " ) ;
2016-01-24 13:48:09 -06:00
StringTokenizer tokens ( request . getURI ( ) , " /? " ) ;
if ( tokens . count ( ) > = 2 & & tokens [ 1 ] = = " convert-to " )
2015-03-04 17:14:04 -06:00
{
2016-01-24 13:48:09 -06:00
std : : string fromPath ;
ConvertToPartHandler handler ( fromPath ) ;
2016-02-24 01:39:23 -06:00
HTMLForm form ( request , request . stream ( ) , handler ) ;
2016-03-13 13:00:19 -05:00
const std : : string format = ( form . has ( " format " ) ? form . get ( " format " ) : " " ) ;
2016-01-24 13:48:09 -06:00
bool sent = false ;
if ( ! fromPath . empty ( ) )
2015-10-16 10:38:24 -05:00
{
2016-01-24 13:48:09 -06:00
if ( ! format . empty ( ) )
2015-10-16 10:38:24 -05:00
{
2016-03-13 13:00:19 -05:00
Log : : info ( " Conversion request for URI [ " + fromPath + " ]. " ) ;
2016-04-03 20:40:14 -05:00
// Request a kit process for this doc.
auto child = getNewChild ( ) ;
if ( ! child )
{
// Let the client know we can't serve now.
response . setStatusAndReason ( Poco : : Net : : HTTPResponse : : HTTP_SERVICE_UNAVAILABLE ) ;
response . setContentLength ( 0 ) ;
response . send ( ) ;
return ;
}
2016-03-19 17:49:36 -05:00
auto uriPublic = DocumentBroker : : sanitizeURI ( fromPath ) ;
const auto docKey = DocumentBroker : : getDocKey ( uriPublic ) ;
2016-04-03 20:40:14 -05:00
auto docBroker = std : : make_shared < DocumentBroker > ( uriPublic , docKey , LOOLWSD : : ChildRoot , child ) ;
2016-03-13 13:00:19 -05:00
// This lock could become a bottleneck.
// In that case, we can use a pool and index by publicPath.
2016-03-22 11:47:05 -05:00
std : : unique_lock < std : : mutex > lock ( docBrokersMutex ) ;
2016-03-13 13:00:19 -05:00
Log : : debug ( " New DocumentBroker for docKey [ " + docKey + " ]. " ) ;
2016-03-22 11:47:05 -05:00
docBrokers . emplace ( docKey , docBroker ) ;
2016-03-13 13:00:19 -05:00
2016-04-02 16:34:51 -05:00
// Request a kit process for this doc.
const std : : string aMessage = " request " + id + " " + docKey + " \n " ;
Log : : debug ( " MasterToBroker: " + aMessage . substr ( 0 , aMessage . length ( ) - 1 ) ) ;
IoUtil : : writeFIFO ( LOOLWSD : : BrokerWritePipe , aMessage ) ;
2016-01-24 13:48:09 -06:00
// Load the document.
std : : shared_ptr < WebSocket > ws ;
2016-04-01 10:33:08 -05:00
auto session = std : : make_shared < MasterProcessSession > ( id , LOOLSession : : Kind : : ToClient , ws , docBroker , nullptr ) ;
2016-03-23 11:55:28 -05:00
session - > setEditLock ( true ) ;
2016-03-13 13:00:19 -05:00
docBroker - > incSessions ( ) ;
lock . unlock ( ) ;
2016-01-24 13:48:09 -06:00
std : : string encodedFrom ;
2016-03-13 13:00:19 -05:00
URI : : encode ( docBroker - > getPublicUri ( ) . getPath ( ) , " " , encodedFrom ) ;
2016-01-24 13:48:09 -06:00
const std : : string load = " load url= " + encodedFrom ;
session - > handleInput ( load . data ( ) , load . size ( ) ) ;
// Convert it to the requested format.
2016-03-13 13:00:19 -05:00
Path toPath ( docBroker - > getPublicUri ( ) . getPath ( ) ) ;
2016-01-24 13:48:09 -06:00
toPath . setExtension ( format ) ;
2016-03-28 06:01:19 -05:00
const std : : string toJailURL = " file:// " + std : : string ( JAILED_DOCUMENT_ROOT ) + toPath . getFileName ( ) ;
2016-01-24 13:48:09 -06:00
std : : string encodedTo ;
2016-02-22 08:30:42 -06:00
URI : : encode ( toJailURL , " " , encodedTo ) ;
2016-03-12 18:29:17 -06:00
std : : string saveas = " saveas url= " + encodedTo + " format= " + format + " options= " ;
2016-01-24 13:48:09 -06:00
session - > handleInput ( saveas . data ( ) , saveas . size ( ) ) ;
2016-01-23 18:41:01 -06:00
2016-01-24 13:48:09 -06:00
// Send it back to the client.
2016-03-28 15:07:02 -05:00
//TODO: Should have timeout to avoid waiting forever.
2016-03-13 13:00:19 -05:00
Poco : : URI resultURL ( session - > getSaveAs ( ) ) ;
if ( ! resultURL . getPath ( ) . empty ( ) )
2016-01-24 13:48:09 -06:00
{
const std : : string mimeType = " application/octet-stream " ;
2016-03-13 13:00:19 -05:00
response . sendFile ( resultURL . getPath ( ) , mimeType ) ;
2016-01-24 13:48:09 -06:00
sent = true ;
2016-01-23 18:41:01 -06:00
}
2016-03-13 13:00:19 -05:00
lock . lock ( ) ;
if ( docBroker - > decSessions ( ) = = 0 )
{
Log : : debug ( " Removing DocumentBroker for docKey [ " + docKey + " ]. " ) ;
2016-03-22 11:47:05 -05:00
docBrokers . erase ( docKey ) ;
2016-03-13 13:00:19 -05:00
}
2015-10-20 08:35:43 -05:00
}
2016-01-23 18:41:01 -06:00
2016-01-24 13:48:09 -06:00
// Clean up the temporary directory the HTMLForm ctor created.
Path tempDirectory ( fromPath ) ;
tempDirectory . setFileName ( " " ) ;
Util : : removeFile ( tempDirectory , /*recursive=*/ true ) ;
2015-10-16 10:38:24 -05:00
}
2016-01-24 13:48:09 -06:00
if ( ! sent )
2015-10-22 10:27:29 -05:00
{
2016-01-24 13:48:09 -06:00
response . setStatus ( HTTPResponse : : HTTP_BAD_REQUEST ) ;
response . setContentLength ( 0 ) ;
response . send ( ) ;
}
}
else if ( tokens . count ( ) > = 2 & & tokens [ 1 ] = = " insertfile " )
{
Log : : info ( " Insert file request. " ) ;
response . set ( " Access-Control-Allow-Origin " , " * " ) ;
response . set ( " Access-Control-Allow-Methods " , " GET, POST, OPTIONS " ) ;
response . set ( " Access-Control-Allow-Headers " , " Origin, X-Requested-With, Content-Type, Accept " ) ;
2015-10-22 10:27:29 -05:00
2016-01-24 13:48:09 -06:00
std : : string tmpPath ;
ConvertToPartHandler handler ( tmpPath ) ;
2016-02-24 01:39:23 -06:00
HTMLForm form ( request , request . stream ( ) , handler ) ;
2015-10-22 10:27:29 -05:00
2016-01-24 13:48:09 -06:00
bool goodRequest = form . has ( " childid " ) & & form . has ( " name " ) ;
std : : string formChildid ( form . get ( " childid " ) ) ;
std : : string formName ( form . get ( " name " ) ) ;
2016-01-13 09:35:55 -06:00
2016-01-24 13:48:09 -06:00
// protect against attempts to inject something funny here
if ( goodRequest & & formChildid . find ( ' / ' ) ! = std : : string : : npos & & formName . find ( ' / ' ) ! = std : : string : : npos )
goodRequest = false ;
2016-01-13 09:35:55 -06:00
2016-01-24 13:48:09 -06:00
if ( goodRequest )
{
try
2016-01-13 09:35:55 -06:00
{
2016-01-24 13:48:09 -06:00
Log : : info ( ) < < " Perform insertfile: " < < formChildid < < " , " < < formName < < Log : : end ;
const std : : string dirPath = LOOLWSD : : ChildRoot + formChildid
2016-03-28 06:01:19 -05:00
+ JAILED_DOCUMENT_ROOT + " insertfile " ;
2016-01-24 13:48:09 -06:00
File ( dirPath ) . createDirectories ( ) ;
std : : string fileName = dirPath + Path : : separator ( ) + form . get ( " name " ) ;
File ( tmpPath ) . moveTo ( fileName ) ;
response . setStatus ( HTTPResponse : : HTTP_OK ) ;
response . send ( ) ;
2015-10-22 10:27:29 -05:00
}
2016-01-24 13:48:09 -06:00
catch ( const IOException & exc )
2015-10-22 10:27:29 -05:00
{
2016-03-07 10:22:39 -06:00
Log : : info ( ) < < " ClientRequestHandler::handlePostRequest: IOException: " < < exc . message ( ) < < Log : : end ;
2015-10-22 10:27:29 -05:00
response . setStatus ( HTTPResponse : : HTTP_BAD_REQUEST ) ;
response . send ( ) ;
}
}
2016-01-24 13:48:09 -06:00
else
2015-10-09 07:55:49 -05:00
{
2016-01-24 13:48:09 -06:00
response . setStatus ( HTTPResponse : : HTTP_BAD_REQUEST ) ;
response . send ( ) ;
}
}
else if ( tokens . count ( ) > = 4 )
{
Log : : info ( " File download request. " ) ;
// The user might request a file to download
const std : : string dirPath = LOOLWSD : : ChildRoot + tokens [ 1 ]
2016-03-28 06:01:19 -05:00
+ JAILED_DOCUMENT_ROOT + tokens [ 2 ] ;
2016-01-24 13:48:09 -06:00
std : : string fileName ;
URI : : decode ( tokens [ 3 ] , fileName ) ;
const std : : string filePath = dirPath + Path : : separator ( ) + fileName ;
Log : : info ( " HTTP request for: " + filePath ) ;
File file ( filePath ) ;
if ( file . exists ( ) )
{
response . set ( " Access-Control-Allow-Origin " , " * " ) ;
2016-02-24 01:39:23 -06:00
HTMLForm form ( request ) ;
2016-01-24 13:48:09 -06:00
std : : string mimeType = " application/octet-stream " ;
if ( form . has ( " mime_type " ) )
mimeType = form . get ( " mime_type " ) ;
response . sendFile ( filePath , mimeType ) ;
Util : : removeFile ( dirPath , true ) ;
2015-10-09 07:55:49 -05:00
}
else
{
2016-01-24 13:48:09 -06:00
response . setStatus ( HTTPResponse : : HTTP_NOT_FOUND ) ;
2015-10-09 07:55:49 -05:00
response . setContentLength ( 0 ) ;
response . send ( ) ;
}
2015-03-04 17:14:04 -06:00
}
2016-01-24 13:48:09 -06:00
else
2015-03-04 17:14:04 -06:00
{
2016-01-24 13:48:09 -06:00
Log : : info ( " Bad request. " ) ;
response . setStatus ( HTTPResponse : : HTTP_BAD_REQUEST ) ;
response . setContentLength ( 0 ) ;
response . send ( ) ;
}
}
2015-04-21 07:06:41 -05:00
2016-01-24 13:48:09 -06:00
void handleGetRequest ( HTTPServerRequest & request , HTTPServerResponse & response , const std : : string & id )
{
2016-03-10 21:33:03 -06:00
Log : : info ( " Starting GET request handler for session [ " + id + " ]. " ) ;
2016-02-15 17:05:24 -06:00
2016-03-13 13:00:19 -05:00
// Remove the leading '/' in the GET URL.
std : : string uri = request . getURI ( ) ;
if ( uri . size ( ) > 0 & & uri [ 0 ] = = ' / ' )
{
uri . erase ( 0 , 1 ) ;
}
2016-03-19 17:49:36 -05:00
const auto uriPublic = DocumentBroker : : sanitizeURI ( uri ) ;
const auto docKey = DocumentBroker : : getDocKey ( uriPublic ) ;
std : : shared_ptr < DocumentBroker > docBroker ;
2016-03-13 12:21:36 -05:00
// This lock could become a bottleneck.
// In that case, we can use a pool and index by publicPath.
2016-03-22 13:27:38 -05:00
std : : unique_lock < std : : mutex > docBrokersLock ( docBrokersMutex ) ;
2016-03-12 18:29:17 -06:00
2016-03-13 12:21:36 -05:00
// Lookup this document.
2016-03-22 11:47:05 -05:00
auto it = docBrokers . find ( docKey ) ;
if ( it ! = docBrokers . end ( ) )
2016-03-13 12:21:36 -05:00
{
// Get the DocumentBroker from the Cache.
Log : : debug ( " Found DocumentBroker for docKey [ " + docKey + " ]. " ) ;
docBroker = it - > second ;
assert ( docBroker ) ;
}
else
{
2016-04-03 20:40:14 -05:00
// Request a kit process for this doc.
auto child = getNewChild ( ) ;
if ( ! child )
{
// Let the client know we can't serve now.
response . setStatusAndReason ( Poco : : Net : : HTTPResponse : : HTTP_SERVICE_UNAVAILABLE ) ;
response . setContentLength ( 0 ) ;
response . send ( ) ;
return ;
}
2016-03-13 12:21:36 -05:00
// Set one we just created.
Log : : debug ( " New DocumentBroker for docKey [ " + docKey + " ]. " ) ;
2016-04-03 20:40:14 -05:00
docBroker = std : : make_shared < DocumentBroker > ( uriPublic , docKey , LOOLWSD : : ChildRoot , child ) ;
2016-03-22 11:47:05 -05:00
docBrokers . emplace ( docKey , docBroker ) ;
2016-03-12 18:29:17 -06:00
}
2016-03-10 21:33:03 -06:00
2016-03-21 18:12:00 -05:00
// Validate the URI and Storage before moving on.
docBroker - > validate ( uriPublic ) ;
2016-03-22 17:46:04 -05:00
Log : : debug ( " Validated [ " + uriPublic . toString ( ) + " ]. " ) ;
2016-03-21 18:12:00 -05:00
2016-04-01 10:33:08 -05:00
// For ToClient sessions, we store incoming messages in a queue and have a separate
// thread that handles them. This is so that we can empty the queue when we get a
// "canceltiles" message.
auto queue = std : : make_shared < BasicTileQueue > ( ) ;
2016-03-13 12:21:36 -05:00
auto ws = std : : make_shared < WebSocket > ( request , response ) ;
2016-04-01 10:33:08 -05:00
auto session = std : : make_shared < MasterProcessSession > ( id , LOOLSession : : Kind : : ToClient , ws , docBroker , queue ) ;
2016-03-13 12:21:36 -05:00
docBroker - > incSessions ( ) ;
2016-03-22 13:27:38 -05:00
docBrokersLock . unlock ( ) ;
2016-03-23 11:55:28 -05:00
docBroker - > addWSSession ( id , session ) ;
unsigned wsSessionsCount = docBroker - > getWSSessionsCount ( ) ;
Log : : warn ( docKey + " , ws_sessions++: " + std : : to_string ( wsSessionsCount ) ) ;
if ( wsSessionsCount = = 1 )
session - > setEditLock ( true ) ;
2016-03-13 12:21:36 -05:00
2016-01-24 13:48:09 -06:00
QueueHandler handler ( queue , session , " wsd_queue_ " + session - > getId ( ) ) ;
2016-01-05 20:29:12 -06:00
2016-01-24 13:48:09 -06:00
Thread queueHandlerThread ;
queueHandlerThread . start ( handler ) ;
2016-03-14 10:53:31 -05:00
bool normalShutdown = false ;
2016-01-24 13:48:09 -06:00
2016-03-27 15:06:22 -05:00
IoUtil : : SocketProcessor ( ws , response ,
[ & session , & queue , & normalShutdown ] ( const std : : vector < char > & payload )
2016-01-24 13:48:09 -06:00
{
2016-03-25 11:10:16 -05:00
time ( & session - > _lastMessageTime ) ;
2016-03-27 13:51:54 -05:00
const auto token = LOOLProtocol : : getFirstToken ( payload ) ;
if ( token = = " disconnect " )
2015-12-27 21:47:39 -06:00
{
2016-03-26 21:56:10 -05:00
normalShutdown = true ;
2016-01-24 13:48:09 -06:00
}
else
{
2016-04-01 10:33:08 -05:00
queue - > put ( payload ) ;
2016-01-24 13:48:09 -06:00
}
2016-03-26 21:56:10 -05:00
return true ;
2016-03-27 15:06:22 -05:00
} ,
[ ] ( ) { return TerminationFlag ; } ,
" Client_ws_ " + id
) ;
2015-06-05 08:12:06 -05:00
2016-03-14 10:53:31 -05:00
if ( docBroker - > getSessionsCount ( ) = = 1 & & ! normalShutdown )
{
2016-03-28 15:07:02 -05:00
//TODO: This isn't this simple. We need to wait for the notification
// of save so Storage can persist the save (if necessary).
// In addition, we shouldn't issue save when opening of the doc fails.
2016-03-14 10:53:31 -05:00
Log : : info ( " Non-deliberate shutdown of the last session, saving the document before tearing down. " ) ;
2016-04-01 10:33:08 -05:00
queue - > put ( " uno .uno:Save " ) ;
2016-03-14 10:53:31 -05:00
}
else
{
Log : : info ( " Clearing the queue. " ) ;
2016-04-01 10:33:08 -05:00
queue - > clear ( ) ;
2016-03-14 10:53:31 -05:00
}
2016-03-23 11:55:28 -05:00
docBroker - > removeWSSession ( id ) ;
wsSessionsCount = docBroker - > getWSSessionsCount ( ) ;
Log : : warn ( docKey + " , ws_sessions--: " + std : : to_string ( wsSessionsCount ) ) ;
2016-03-22 13:27:38 -05:00
2016-03-14 10:53:31 -05:00
Log : : info ( " Finishing GET request handler for session [ " + id + " ]. Joining the queue. " ) ;
2016-04-01 10:33:08 -05:00
queue - > put ( " eof " ) ;
2016-01-24 13:48:09 -06:00
queueHandlerThread . join ( ) ;
2016-03-12 18:29:17 -06:00
2016-03-22 13:27:38 -05:00
docBrokersLock . lock ( ) ;
2016-03-13 09:04:54 -05:00
if ( docBroker - > decSessions ( ) = = 0 )
{
2016-03-13 12:21:36 -05:00
Log : : debug ( " Removing DocumentBroker for docKey [ " + docKey + " ]. " ) ;
2016-03-22 11:47:05 -05:00
docBrokers . erase ( docKey ) ;
2016-03-13 09:04:54 -05:00
}
2016-01-24 13:48:09 -06:00
}
2016-03-16 18:57:18 -05:00
void handleGetDiscovery ( HTTPServerRequest & request , HTTPServerResponse & response )
2016-03-15 10:35:59 -05:00
{
DOMParser parser ;
DOMWriter writer ;
2016-03-16 18:57:18 -05:00
URI uri ( " http " , request . getHost ( ) , request . getURI ( ) ) ;
2016-03-15 18:13:23 -05:00
2016-03-16 18:57:18 -05:00
const std : : string discoveryPath = Path ( Application : : instance ( ) . commandPath ( ) ) . parent ( ) . toString ( ) + " discovery.xml " ;
2016-03-15 10:35:59 -05:00
const std : : string mediaType = " text/xml " ;
const std : : string action = " action " ;
const std : : string urlsrc = " urlsrc " ;
2016-04-01 09:31:33 -05:00
const std : : string uriValue = " https:// " + uri . getHost ( ) + " : " + std : : to_string ( uri . getPort ( ) ) + " /loleaflet/dist/loleaflet.html? " ;
2016-03-15 10:35:59 -05:00
InputSource inputSrc ( discoveryPath ) ;
AutoPtr < Poco : : XML : : Document > docXML = parser . parse ( & inputSrc ) ;
AutoPtr < NodeList > listNodes = docXML - > getElementsByTagName ( action ) ;
for ( unsigned long it = 0 ; it < listNodes - > length ( ) ; it + + )
{
static_cast < Element * > ( listNodes - > item ( it ) ) - > setAttribute ( urlsrc , uriValue ) ;
}
std : : ostringstream ostrXML ;
writer . writeNode ( ostrXML , docXML ) ;
response . set ( " User-Agent " , " LOOLWSD WOPI Agent " ) ;
response . setContentLength ( ostrXML . str ( ) . length ( ) ) ;
response . setContentType ( mediaType ) ;
response . setChunkedTransferEncoding ( false ) ;
std : : ostream & ostr = response . send ( ) ;
ostr < < ostrXML . str ( ) ;
}
2016-01-24 13:48:09 -06:00
public :
void handleRequest ( HTTPServerRequest & request , HTTPServerResponse & response ) override
{
const auto id = LOOLWSD : : GenSessionId ( ) ;
const std : : string thread_name = " client_ws_ " + id ;
if ( prctl ( PR_SET_NAME , reinterpret_cast < unsigned long > ( thread_name . c_str ( ) ) , 0 , 0 , 0 ) ! = 0 )
Log : : error ( " Cannot set thread name to " + thread_name + " . " ) ;
2016-02-29 06:25:12 -06:00
2016-01-24 13:48:09 -06:00
Log : : debug ( " Thread [ " + thread_name + " ] started. " ) ;
try
{
2016-03-15 10:35:59 -05:00
if ( request . getMethod ( ) = = HTTPRequest : : HTTP_GET & & request . getURI ( ) = = " /hosting/discovery " )
{
// http://server/hosting/discovery
2016-03-16 18:57:18 -05:00
handleGetDiscovery ( request , response ) ;
2016-03-15 10:35:59 -05:00
}
else if ( ! ( request . find ( " Upgrade " ) ! = request . end ( ) & & Poco : : icompare ( request [ " Upgrade " ] , " websocket " ) = = 0 ) )
2016-01-24 13:48:09 -06:00
{
handlePostRequest ( request , response , id ) ;
}
else
{
2016-02-15 17:05:24 -06:00
//authenticate(request, response, id);
2016-01-24 13:48:09 -06:00
handleGetRequest ( request , response , id ) ;
}
2016-01-05 20:29:12 -06:00
}
2016-01-06 08:13:21 -06:00
catch ( const Exception & exc )
2016-01-05 20:29:12 -06:00
{
2016-03-07 10:22:39 -06:00
Log : : error ( ) < < " ClientRequestHandler::handleRequest: Exception: " < < exc . displayText ( )
2016-01-06 08:13:21 -06:00
< < ( exc . nested ( ) ? " ( " + exc . nested ( ) - > displayText ( ) + " ) " : " " )
< < Log : : end ;
}
catch ( const std : : exception & exc )
{
2016-03-07 10:22:39 -06:00
Log : : error ( std : : string ( " ClientRequestHandler::handleRequest: Exception: " ) + exc . what ( ) ) ;
2016-01-06 08:13:21 -06:00
}
catch ( . . . )
{
2016-03-07 10:22:39 -06:00
Log : : error ( " ClientRequestHandler::handleRequest: Unexpected exception " ) ;
2016-01-05 20:29:12 -06:00
}
2015-04-21 07:06:41 -05:00
2016-01-05 20:29:12 -06:00
Log : : debug ( " Thread [ " + thread_name + " ] finished. " ) ;
}
} ;
2015-11-25 20:23:08 -06:00
2016-01-05 20:29:12 -06:00
/// Handle requests from prisoners (internal).
class PrisonerRequestHandler : public HTTPRequestHandler
{
public :
2015-04-21 07:06:41 -05:00
2016-01-05 20:29:12 -06:00
void handleRequest ( HTTPServerRequest & request , HTTPServerResponse & response ) override
{
2016-04-03 09:31:46 -05:00
std : : string thread_name = " prison_ws_ " ;
if ( prctl ( PR_SET_NAME , reinterpret_cast < unsigned long > ( thread_name . c_str ( ) ) , 0 , 0 , 0 ) ! = 0 )
Log : : error ( " Cannot set thread name to " + thread_name + " . " ) ;
Log : : debug ( " Child connection with URI [ " + request . getURI ( ) + " ]. " ) ;
2016-01-05 20:29:12 -06:00
assert ( request . serverAddress ( ) . port ( ) = = MASTER_PORT_NUMBER ) ;
2016-04-03 09:33:35 -05:00
if ( request . getURI ( ) . find ( NEW_CHILD_URI ) = = 0 )
{
// New Child is spawned.
const auto params = Poco : : URI ( request . getURI ( ) ) . getQueryParameters ( ) ;
Poco : : Process : : PID pid = - 1 ;
for ( const auto & param : params )
{
if ( param . first = = " pid " )
{
pid = std : : stoi ( param . second ) ;
}
}
if ( pid < = 0 )
{
Log : : error ( " Invalid PID in child URI [ " + request . getURI ( ) + " ]. " ) ;
return ;
}
Log : : info ( " New child [ " + std : : to_string ( pid ) + " ]. " ) ;
auto ws = std : : make_shared < WebSocket > ( request , response ) ;
std : : unique_lock < std : : mutex > lock ( newChildsMutex ) ;
newChilds . emplace_back ( std : : make_shared < ChildProcess > ( pid , ws ) ) ;
2016-04-03 20:40:14 -05:00
Log : : info ( " Have " + std : : to_string ( newChilds . size ( ) ) + " childs. " ) ;
2016-04-03 09:33:35 -05:00
return ;
}
2016-03-29 06:18:30 -05:00
if ( request . getURI ( ) . find ( CHILD_URI ) ! = 0 )
{
Log : : error ( " Invalid request URI: [ " + request . getURI ( ) + " ]. " ) ;
return ;
}
2015-06-09 10:04:46 -05:00
2016-01-10 09:35:28 -06:00
try
{
2016-03-12 07:46:36 -06:00
const auto params = Poco : : URI ( request . getURI ( ) ) . getQueryParameters ( ) ;
std : : string sessionId ;
std : : string jailId ;
2016-03-12 17:12:40 -06:00
std : : string docKey ;
2016-03-12 07:46:36 -06:00
for ( const auto & param : params )
{
if ( param . first = = " sessionId " )
{
sessionId = param . second ;
}
else if ( param . first = = " jailId " )
{
jailId = param . second ;
}
2016-03-12 17:12:40 -06:00
else if ( param . first = = " docKey " )
{
docKey = param . second ;
}
2016-03-12 07:46:36 -06:00
}
2016-01-10 09:35:28 -06:00
2016-03-12 07:46:36 -06:00
thread_name + = sessionId ;
2016-01-10 09:35:28 -06:00
if ( prctl ( PR_SET_NAME , reinterpret_cast < unsigned long > ( thread_name . c_str ( ) ) , 0 , 0 , 0 ) ! = 0 )
Log : : error ( " Cannot set thread name to " + thread_name + " . " ) ;
2016-02-29 06:25:12 -06:00
2016-01-10 09:35:28 -06:00
Log : : debug ( " Thread [ " + thread_name + " ] started. " ) ;
2015-12-30 11:10:31 -06:00
2016-03-12 17:12:40 -06:00
Log : : debug ( " Child socket for SessionId: " + sessionId + " , jailId: " + jailId +
" , docKey: " + docKey + " connected. " ) ;
2016-03-12 18:29:17 -06:00
std : : shared_ptr < DocumentBroker > docBroker ;
{
// This lock could become a bottleneck.
// In that case, we can use a pool and index by publicPath.
2016-03-22 11:47:05 -05:00
std : : unique_lock < std : : mutex > lock ( docBrokersMutex ) ;
2016-03-12 18:29:17 -06:00
// Lookup this document.
2016-03-22 11:47:05 -05:00
auto it = docBrokers . find ( docKey ) ;
if ( it ! = docBrokers . end ( ) )
2016-03-12 18:29:17 -06:00
{
// Get the DocumentBroker from the Cache.
docBroker = it - > second ;
assert ( docBroker ) ;
}
else
{
// The client closed before we started,
// or some early failure happened.
Log : : error ( " Failed to find DocumentBroker for docKey [ " + docKey +
" ] while handling child connection for session [ " + sessionId + " ]. " ) ;
throw std : : runtime_error ( " Invalid docKey. " ) ;
}
}
2016-03-12 19:39:34 -06:00
docBroker - > load ( jailId ) ;
2016-01-05 20:29:12 -06:00
auto ws = std : : make_shared < WebSocket > ( request , response ) ;
2016-04-01 10:33:08 -05:00
auto session = std : : make_shared < MasterProcessSession > ( sessionId , LOOLSession : : Kind : : ToPrisoner , ws , docBroker , nullptr ) ;
2016-03-12 08:07:33 -06:00
std : : unique_lock < std : : mutex > lock ( MasterProcessSession : : AvailableChildSessionMutex ) ;
MasterProcessSession : : AvailableChildSessions . emplace ( sessionId , session ) ;
Log : : info ( ) < < " mapped " < < session < < " jailId= " < < jailId < < " , id= " < < sessionId
< < " into _availableChildSessions, size= " < < MasterProcessSession : : AvailableChildSessions . size ( ) < < Log : : end ;
lock . unlock ( ) ;
MasterProcessSession : : AvailableChildSessionCV . notify_one ( ) ;
2016-01-05 20:29:12 -06:00
2016-03-27 15:06:22 -05:00
IoUtil : : SocketProcessor ( ws , response ,
[ & session ] ( const std : : vector < char > & payload )
2015-04-21 07:06:41 -05:00
{
2016-03-26 21:56:10 -05:00
return session - > handleInput ( payload . data ( ) , payload . size ( ) ) ;
2016-03-27 15:06:22 -05:00
} ,
[ ] ( ) { return TerminationFlag ; } ,
" Child_ws_ " + sessionId
) ;
2015-03-04 17:14:04 -06:00
}
2016-01-06 08:13:21 -06:00
catch ( const Exception & exc )
{
2016-03-07 10:22:39 -06:00
Log : : error ( ) < < " PrisonerRequestHandler::handleRequest: Exception: " < < exc . displayText ( )
2016-01-06 08:13:21 -06:00
< < ( exc . nested ( ) ? " ( " + exc . nested ( ) - > displayText ( ) + " ) " : " " )
< < Log : : end ;
}
catch ( const std : : exception & exc )
{
2016-03-07 10:22:39 -06:00
Log : : error ( std : : string ( " PrisonerRequestHandler::handleRequest: Exception: " ) + exc . what ( ) ) ;
2016-01-06 08:13:21 -06:00
}
catch ( . . . )
2015-03-04 17:14:04 -06:00
{
2016-03-07 10:22:39 -06:00
Log : : error ( " PrisonerRequestHandler::handleRequest: Unexpected exception " ) ;
2015-03-04 17:14:04 -06:00
}
2015-12-26 10:33:01 -06:00
Log : : debug ( " Thread [ " + thread_name + " ] finished. " ) ;
2015-03-04 17:14:04 -06:00
}
} ;
2016-03-20 05:59:32 -05:00
class ClientRequestHandlerFactory : public HTTPRequestHandlerFactory
2015-03-04 17:14:04 -06:00
{
public :
2016-03-20 05:59:32 -05:00
ClientRequestHandlerFactory ( Admin & admin , FileServer & fileServer )
: _admin ( admin ) ,
_fileServer ( fileServer )
{ }
2015-03-04 17:14:04 -06:00
HTTPRequestHandler * createRequestHandler ( const HTTPServerRequest & request ) override
{
2016-01-23 18:41:01 -06:00
if ( prctl ( PR_SET_NAME , reinterpret_cast < unsigned long > ( " request_handler " ) , 0 , 0 , 0 ) ! = 0 )
Log : : error ( " Cannot set thread name to request_handler. " ) ;
2016-01-19 19:38:43 -06:00
2015-12-25 11:35:23 -06:00
auto logger = Log : : info ( ) ;
logger < < " Request from " < < request . clientAddress ( ) . toString ( ) < < " : "
< < request . getMethod ( ) < < " " < < request . getURI ( ) < < " "
< < request . getVersion ( ) ;
2015-03-07 05:23:46 -06:00
2015-03-04 17:14:04 -06:00
for ( HTTPServerRequest : : ConstIterator it = request . begin ( ) ; it ! = request . end ( ) ; + + it )
{
2015-12-25 11:35:23 -06:00
logger < < " / " < < it - > first < < " : " < < it - > second ;
2015-03-04 17:14:04 -06:00
}
2015-03-07 05:23:46 -06:00
2015-12-25 11:35:23 -06:00
logger < < Log : : end ;
2016-03-20 05:59:32 -05:00
// Routing
// FIXME: Some browsers (all?) hit for /favicon.ico. Create a nice favicon and add to routes
Poco : : URI requestUri ( request . getURI ( ) ) ;
std : : vector < std : : string > reqPathSegs ;
requestUri . getPathSegments ( reqPathSegs ) ;
HTTPRequestHandler * requestHandler ;
// File server
if ( reqPathSegs . size ( ) > = 1 & & reqPathSegs [ 0 ] = = " loleaflet " )
{
requestHandler = _fileServer . createRequestHandler ( ) ;
}
// Admin WebSocket Connections
else if ( reqPathSegs . size ( ) > = 1 & & reqPathSegs [ 0 ] = = " adminws " )
{
requestHandler = _admin . createRequestHandler ( ) ;
}
// Client post and websocket connections
else
{
requestHandler = new ClientRequestHandler ( ) ;
}
return requestHandler ;
}
private :
Admin & _admin ;
FileServer & _fileServer ;
} ;
class PrisonerRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public :
HTTPRequestHandler * createRequestHandler ( const HTTPServerRequest & request ) override
{
if ( prctl ( PR_SET_NAME , reinterpret_cast < unsigned long > ( " request_handler " ) , 0 , 0 , 0 ) ! = 0 )
Log : : error ( " Cannot set thread name to request_handler. " ) ;
auto logger = Log : : info ( ) ;
logger < < " Request from " < < request . clientAddress ( ) . toString ( ) < < " : "
< < request . getMethod ( ) < < " " < < request . getURI ( ) < < " "
< < request . getVersion ( ) ;
for ( HTTPServerRequest : : ConstIterator it = request . begin ( ) ; it ! = request . end ( ) ; + + it )
{
logger < < " / " < < it - > first < < " : " < < it - > second ;
}
logger < < Log : : end ;
return new PrisonerRequestHandler ( ) ;
2015-03-04 17:14:04 -06:00
}
} ;
2015-05-29 00:49:49 -05:00
class TestOutput : public Runnable
2015-03-04 17:14:04 -06:00
{
public :
TestOutput ( WebSocket & ws ) :
_ws ( ws )
{
}
void run ( ) override
{
int flags ;
int n ;
2015-03-27 09:53:33 -05:00
_ws . setReceiveTimeout ( 0 ) ;
2015-03-12 11:58:51 -05:00
try
2015-03-04 17:14:04 -06:00
{
2015-03-12 11:58:51 -05:00
do
2015-03-04 17:14:04 -06:00
{
2015-10-16 08:23:49 -05:00
char buffer [ 200000 ] ;
2015-03-12 11:58:51 -05:00
n = _ws . receiveFrame ( buffer , sizeof ( buffer ) , flags ) ;
2016-02-23 12:03:43 -06:00
if ( n > 0 & & ( flags & WebSocket : : FRAME_OP_BITMASK ) ! = WebSocket : : FRAME_OP_CLOSE )
2015-03-12 11:58:51 -05:00
{
2015-12-25 12:50:53 -06:00
Log : : trace ( ) < < " Client got " < < n < < " bytes: "
< < getAbbreviatedMessage ( buffer , n ) < < Log : : end ;
2015-03-12 11:58:51 -05:00
}
2015-03-04 17:14:04 -06:00
}
2015-03-12 11:58:51 -05:00
while ( n > 0 & & ( flags & WebSocket : : FRAME_OP_BITMASK ) ! = WebSocket : : FRAME_OP_CLOSE ) ;
}
2015-12-25 12:50:53 -06:00
catch ( const WebSocketException & exc )
2015-03-12 11:58:51 -05:00
{
2015-12-20 11:59:26 -06:00
Log : : error ( " TestOutput::run(), WebSocketException: " + exc . message ( ) ) ;
2015-03-12 11:58:51 -05:00
_ws . close ( ) ;
2015-03-04 17:14:04 -06:00
}
}
private :
WebSocket & _ws ;
} ;
2015-05-29 00:49:49 -05:00
class TestInput : public Runnable
2015-03-04 17:14:04 -06:00
{
public :
TestInput ( ServerApplication & main , ServerSocket & svs , HTTPServer & srv ) :
_main ( main ) ,
_svs ( svs ) ,
_srv ( srv )
{
}
void run ( ) override
{
2015-04-23 10:52:16 -05:00
HTTPClientSession cs ( " 127.0.0.1 " , _svs . address ( ) . port ( ) ) ;
2015-03-04 17:14:04 -06:00
HTTPRequest request ( HTTPRequest : : HTTP_GET , " /ws " ) ;
HTTPResponse response ;
WebSocket ws ( cs , request , response ) ;
Thread thread ;
TestOutput output ( ws ) ;
thread . start ( output ) ;
2015-03-17 18:56:15 -05:00
if ( isatty ( 0 ) )
{
std : : cout < < std : : endl ;
std : : cout < < " Enter LOOL WS requests, one per line. Enter EOF to finish. " < < std : : endl ;
}
2015-03-04 17:14:04 -06:00
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 ;
} ;
2015-12-27 21:47:39 -06:00
std : : atomic < unsigned > LOOLWSD : : NextSessionId ;
2016-04-03 22:40:56 -05:00
int LOOLWSD : : BrokerWritePipe = - 1 ;
2016-01-15 02:40:54 -06:00
std : : string LOOLWSD : : Cache = LOOLWSD_CACHEDIR ;
std : : string LOOLWSD : : SysTemplate ;
std : : string LOOLWSD : : LoTemplate ;
std : : string LOOLWSD : : ChildRoot ;
std : : string LOOLWSD : : LoSubPath = " lo " ;
2016-03-20 09:07:24 -05:00
std : : string LOOLWSD : : FileServerRoot ;
2015-07-13 09:13:06 -05:00
2015-12-28 15:34:21 -06:00
int LOOLWSD : : NumPreSpawnedChildren = 10 ;
2016-01-15 02:40:54 -06:00
bool LOOLWSD : : DoTest = false ;
2016-03-28 05:42:07 -05:00
static const std : : string pidLog = " /tmp/loolwsd.pid " ;
2015-03-17 18:56:15 -05:00
2015-11-25 21:17:08 -06:00
LOOLWSD : : LOOLWSD ( )
2015-03-04 17:14:04 -06:00
{
2015-03-17 18:56:15 -05:00
}
2015-03-07 05:23:46 -06:00
2015-03-17 18:56:15 -05:00
LOOLWSD : : ~ LOOLWSD ( )
{
}
2015-03-04 17:14:04 -06:00
2015-03-17 18:56:15 -05:00
void LOOLWSD : : initialize ( Application & self )
{
2016-03-16 14:52:01 -05:00
// load default configuration files, if present
2016-03-24 04:37:17 -05:00
if ( loadConfiguration ( ) = = 0 )
{
2016-03-28 05:38:36 -05:00
std : : string configPath = LOOLWSD_CONFIGDIR " /loolwsd.xml " ;
2016-03-24 04:37:17 -05:00
loadConfiguration ( configPath ) ;
}
2016-03-16 14:52:01 -05:00
2015-03-17 18:56:15 -05:00
ServerApplication : : initialize ( self ) ;
}
2015-03-07 05:23:46 -06:00
2016-03-23 06:08:01 -05:00
void LOOLWSD : : initializeSSL ( )
{
auto & conf = config ( ) ;
auto ssl_cert_file_path = conf . getString ( " ssl.cert_file_path " ) ;
if ( conf . getBool ( " ssl.cert_file_path[@relative] " ) )
{
ssl_cert_file_path = Poco : : Path ( Application : : instance ( ) . commandPath ( ) ) . parent ( ) . append ( ssl_cert_file_path ) . toString ( ) ;
}
Log : : info ( " SSL Cert file: " + ssl_cert_file_path ) ;
auto ssl_key_file_path = conf . getString ( " ssl.key_file_path " ) ;
if ( conf . getBool ( " ssl.key_file_path[@relative] " ) )
{
ssl_key_file_path = Poco : : Path ( Application : : instance ( ) . commandPath ( ) ) . parent ( ) . append ( ssl_key_file_path ) . toString ( ) ;
}
Log : : info ( " SSL Key file: " + ssl_key_file_path ) ;
2016-03-24 21:42:37 -05:00
auto ssl_ca_file_path = conf . getString ( " ssl.ca_file_path " ) ;
if ( conf . getBool ( " ssl.ca_file_path[@relative] " ) )
{
ssl_ca_file_path = Poco : : Path ( Application : : instance ( ) . commandPath ( ) ) . parent ( ) . append ( ssl_ca_file_path ) . toString ( ) ;
}
Log : : info ( " SSL CA file: " + ssl_ca_file_path ) ;
2016-03-23 06:08:01 -05:00
Poco : : Crypto : : initializeCrypto ( ) ;
Poco : : Net : : initializeSSL ( ) ;
Poco : : Net : : Context : : Params sslParams ;
sslParams . certificateFile = ssl_cert_file_path ;
sslParams . privateKeyFile = ssl_key_file_path ;
2016-03-24 21:42:37 -05:00
sslParams . caLocation = ssl_ca_file_path ;
2016-03-23 06:08:01 -05:00
// Don't ask clients for certificate
sslParams . verificationMode = Poco : : Net : : Context : : VERIFY_NONE ;
Poco : : SharedPtr < Poco : : Net : : PrivateKeyPassphraseHandler > consoleHandler = new Poco : : Net : : KeyConsoleHandler ( true ) ;
2016-03-30 08:53:27 -05:00
Poco : : SharedPtr < Poco : : Net : : InvalidCertificateHandler > invalidCertHandler = new Poco : : Net : : ConsoleCertificateHandler ( true ) ;
2016-03-23 06:08:01 -05:00
Poco : : Net : : Context : : Ptr sslContext = new Poco : : Net : : Context ( Poco : : Net : : Context : : SERVER_USE , sslParams ) ;
Poco : : Net : : SSLManager : : instance ( ) . initializeServer ( consoleHandler , invalidCertHandler , sslContext ) ;
2016-03-30 10:57:17 -05:00
// Init client
Poco : : Net : : Context : : Params sslClientParams ;
// TODO: Be more strict and setup SSL key/certs for owncloud server and us
sslClientParams . verificationMode = Poco : : Net : : Context : : VERIFY_NONE ;
Poco : : SharedPtr < Poco : : Net : : PrivateKeyPassphraseHandler > consoleClientHandler = new Poco : : Net : : KeyConsoleHandler ( false ) ;
Poco : : SharedPtr < Poco : : Net : : InvalidCertificateHandler > invalidClientCertHandler = new Poco : : Net : : AcceptCertificateHandler ( false ) ;
Poco : : Net : : Context : : Ptr sslClientContext = new Poco : : Net : : Context ( Poco : : Net : : Context : : CLIENT_USE , sslClientParams ) ;
Poco : : Net : : SSLManager : : instance ( ) . initializeClient ( consoleClientHandler , invalidClientCertHandler , sslClientContext ) ;
2016-03-23 06:08:01 -05:00
}
2015-03-17 18:56:15 -05:00
void LOOLWSD : : uninitialize ( )
{
ServerApplication : : uninitialize ( ) ;
}
2015-10-28 04:55:03 -05:00
void LOOLWSD : : defineOptions ( OptionSet & optionSet )
2015-03-17 18:56:15 -05:00
{
2015-10-28 04:55:03 -05:00
ServerApplication : : defineOptions ( optionSet ) ;
2015-03-17 18:56:15 -05:00
2015-10-28 04:55:03 -05:00
optionSet . addOption ( Option ( " help " , " " , " Display help information on command line arguments. " )
. required ( false )
. repeatable ( false ) ) ;
2015-03-17 18:56:15 -05:00
2015-12-19 12:38:44 -06:00
optionSet . addOption ( Option ( " version " , " " , " Display version information. " )
. required ( false )
. repeatable ( false ) ) ;
2015-10-28 04:55:03 -05:00
optionSet . addOption ( Option ( " port " , " " , " Port number to listen to (default: " + std : : to_string ( DEFAULT_CLIENT_PORT_NUMBER ) + " ), "
2015-05-08 13:29:13 -05:00
" must not be " + std : : to_string ( MASTER_PORT_NUMBER ) + " . " )
2015-10-28 04:55:03 -05:00
. required ( false )
. repeatable ( false )
. argument ( " port number " ) ) ;
optionSet . addOption ( Option ( " cache " , " " , " Path to a directory where to keep the persistent tile cache (default: " + std : : string ( LOOLWSD_CACHEDIR ) + " ). " )
. required ( false )
. repeatable ( false )
. argument ( " directory " ) ) ;
optionSet . addOption ( Option ( " systemplate " , " " , " Path to a template tree with shared libraries etc to be used as source for chroot jails for child processes. " )
. required ( false )
. repeatable ( false )
. argument ( " directory " ) ) ;
optionSet . addOption ( Option ( " lotemplate " , " " , " Path to a LibreOffice installation tree to be copied (linked) into the jails for child processes. Should be on the same file system as systemplate. " )
. required ( false )
. repeatable ( false )
. argument ( " directory " ) ) ;
optionSet . addOption ( Option ( " childroot " , " " , " Path to the directory under which the chroot jails for the child processes will be created. Should be on the same file system as systemplate and lotemplate. " )
. required ( false )
. repeatable ( false )
. argument ( " directory " ) ) ;
2016-01-15 02:40:54 -06:00
optionSet . addOption ( Option ( " losubpath " , " " , " Relative path where the LibreOffice installation will be copied inside a jail (default: ' " + LoSubPath + " '). " )
2015-10-28 04:55:03 -05:00
. required ( false )
. repeatable ( false )
. argument ( " relative path " ) ) ;
2016-03-20 09:07:24 -05:00
optionSet . addOption ( Option ( " fileserverroot " , " " , " Path to the directory that should be considered root for the file server (default: '../loleaflet/'). " )
. required ( false )
. repeatable ( false )
. argument ( " directory " ) ) ;
2015-10-28 04:55:03 -05:00
optionSet . addOption ( Option ( " numprespawns " , " " , " Number of child processes to keep started in advance and waiting for new clients. " )
. required ( false )
. repeatable ( false )
. argument ( " number " ) ) ;
optionSet . addOption ( Option ( " test " , " " , " Interactive testing. " )
. required ( false )
. repeatable ( false ) ) ;
2015-03-17 18:56:15 -05:00
}
2015-10-28 04:55:03 -05:00
void LOOLWSD : : handleOption ( const std : : string & optionName , const std : : string & value )
2015-03-25 07:39:58 -05:00
{
2015-10-28 04:55:03 -05:00
ServerApplication : : handleOption ( optionName , value ) ;
2015-03-25 07:39:58 -05:00
2015-10-28 04:55:03 -05:00
if ( optionName = = " help " )
2015-03-25 07:39:58 -05:00
{
displayHelp ( ) ;
2016-03-02 00:47:13 -06:00
std : : exit ( Application : : EXIT_OK ) ;
2015-03-25 07:39:58 -05:00
}
2015-12-19 12:38:44 -06:00
else if ( optionName = = " version " )
{
displayVersion ( ) ;
2016-03-02 00:47:13 -06:00
std : : exit ( Application : : EXIT_OK ) ;
2015-12-19 12:38:44 -06:00
}
2015-10-28 04:55:03 -05:00
else if ( optionName = = " port " )
2015-12-27 16:46:42 -06:00
ClientPortNumber = std : : stoi ( value ) ;
2015-10-28 04:55:03 -05:00
else if ( optionName = = " cache " )
2016-01-15 02:40:54 -06:00
Cache = value ;
2015-10-28 04:55:03 -05:00
else if ( optionName = = " systemplate " )
2016-01-15 02:40:54 -06:00
SysTemplate = value ;
2015-10-28 04:55:03 -05:00
else if ( optionName = = " lotemplate " )
2016-01-15 02:40:54 -06:00
LoTemplate = value ;
2015-10-28 04:55:03 -05:00
else if ( optionName = = " childroot " )
2016-01-15 02:40:54 -06:00
ChildRoot = value ;
2015-10-28 04:55:03 -05:00
else if ( optionName = = " losubpath " )
2016-01-15 02:40:54 -06:00
LoSubPath = value ;
2016-03-20 09:07:24 -05:00
else if ( optionName = = " fileserverroot " )
FileServerRoot = value ;
2015-10-28 04:55:03 -05:00
else if ( optionName = = " numprespawns " )
2015-12-28 15:34:21 -06:00
NumPreSpawnedChildren = std : : stoi ( value ) ;
2015-10-28 04:55:03 -05:00
else if ( optionName = = " test " )
2016-01-15 02:40:54 -06:00
LOOLWSD : : DoTest = true ;
2015-03-25 07:39:58 -05:00
}
void LOOLWSD : : displayHelp ( )
{
HelpFormatter helpFormatter ( options ( ) ) ;
helpFormatter . setCommand ( commandName ( ) ) ;
helpFormatter . setUsage ( " OPTIONS " ) ;
helpFormatter . setHeader ( " LibreOffice On-Line WebSocket server. " ) ;
helpFormatter . format ( std : : cout ) ;
}
2015-03-17 18:56:15 -05:00
2015-12-19 12:38:44 -06:00
void LOOLWSD : : displayVersion ( )
{
std : : cout < < LOOLWSD_VERSION < < std : : endl ;
}
2016-02-24 01:39:23 -06:00
Process : : PID LOOLWSD : : createBroker ( )
2015-07-13 09:13:06 -05:00
{
2016-04-03 22:40:56 -05:00
Process : : Args args ;
2015-12-19 19:09:48 -06:00
2016-04-03 22:40:56 -05:00
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 ) ) ;
args . push_back ( " --clientport= " + std : : to_string ( ClientPortNumber ) ) ;
2015-12-19 19:09:48 -06:00
2016-04-03 22:40:56 -05:00
const std : : string brokerPath = Path ( Application : : instance ( ) . commandPath ( ) ) . parent ( ) . toString ( ) + " loolbroker " ;
2015-12-19 19:09:48 -06:00
2016-04-03 22:40:56 -05:00
Log : : info ( " Launching Broker #1: " + brokerPath + " " +
Poco : : cat ( std : : string ( " " ) , args . begin ( ) , args . end ( ) ) ) ;
2015-12-19 19:09:48 -06:00
2016-04-03 22:40:56 -05:00
ProcessHandle child = Process : : launch ( brokerPath , args ) ;
2015-12-19 19:09:48 -06:00
2016-04-03 22:40:56 -05:00
return child . id ( ) ;
2015-07-13 09:13:06 -05:00
}
2015-10-16 10:45:03 -05:00
int LOOLWSD : : main ( const std : : vector < std : : string > & /*args*/ )
2015-07-13 09:13:06 -05:00
{
2015-12-28 17:24:29 -06:00
Log : : initialize ( " wsd " ) ;
2016-02-29 06:25:12 -06:00
2016-02-29 06:06:48 -06:00
if ( geteuid ( ) = = 0 )
{
Log : : error ( " Don't run this as root " ) ;
return Application : : EXIT_USAGE ;
}
2016-02-29 06:25:12 -06:00
2016-03-23 06:08:01 -05:00
initializeSSL ( ) ;
2016-03-21 03:37:39 -05:00
2015-12-29 19:34:53 -06:00
char * locale = setlocale ( LC_ALL , nullptr ) ;
if ( locale = = nullptr | | std : : strcmp ( locale , " C " ) = = 0 )
2015-10-13 12:05:42 -05:00
setlocale ( LC_ALL , " en_US.utf8 " ) ;
2016-02-01 19:26:19 -06:00
Util : : setTerminationSignals ( ) ;
Util : : setFatalSignals ( ) ;
2016-01-09 12:51:55 -06:00
2016-01-15 02:40:54 -06:00
if ( access ( Cache . c_str ( ) , R_OK | W_OK | X_OK ) ! = 0 )
2015-07-17 16:55:27 -05:00
{
2016-01-15 02:40:54 -06:00
Log : : error ( " Unable to access cache [ " + Cache +
2015-12-25 12:50:53 -06:00
" ] please make sure it exists, and has write permission for this user. " ) ;
2016-02-01 19:51:42 -06:00
return Application : : EXIT_SOFTWARE ;
2015-07-17 16:55:27 -05:00
}
// We use the same option set for both parent and child loolwsd,
// so must check options required in the parent (but not in the
// child) separately now. Also check for options that are
// meaningless for the parent.
2016-01-15 02:40:54 -06:00
if ( SysTemplate . empty ( ) )
2015-07-17 16:55:27 -05:00
throw MissingOptionException ( " systemplate " ) ;
2016-01-15 02:40:54 -06:00
if ( LoTemplate . empty ( ) )
2015-07-17 16:55:27 -05:00
throw MissingOptionException ( " lotemplate " ) ;
2016-01-04 15:15:02 -06:00
2016-01-15 02:40:54 -06:00
if ( ChildRoot . empty ( ) )
2015-07-17 16:55:27 -05:00
throw MissingOptionException ( " childroot " ) ;
2016-01-15 02:40:54 -06:00
else if ( ChildRoot [ ChildRoot . size ( ) - 1 ] ! = Path : : separator ( ) )
ChildRoot + = Path : : separator ( ) ;
2015-07-17 16:55:27 -05:00
2016-03-20 09:07:24 -05:00
if ( FileServerRoot . empty ( ) )
FileServerRoot = Path ( Application : : instance ( ) . commandPath ( ) ) . parent ( ) . parent ( ) . toString ( ) ;
2015-12-27 16:46:42 -06:00
if ( ClientPortNumber = = MASTER_PORT_NUMBER )
2015-07-17 16:55:27 -05:00
throw IncompatibleOptionsException ( " port " ) ;
2016-01-15 02:40:54 -06:00
if ( LOOLWSD : : DoTest )
2015-12-28 15:34:21 -06:00
NumPreSpawnedChildren = 1 ;
2015-07-17 16:55:27 -05:00
2015-07-19 15:49:11 -05:00
// log pid information
{
2016-03-28 05:42:07 -05:00
FileOutputStream filePID ( pidLog ) ;
2015-07-19 15:49:11 -05:00
if ( filePID . good ( ) )
filePID < < Process : : id ( ) ;
}
2016-02-13 11:22:15 -06:00
const Path pipePath = Path : : forDirectory ( ChildRoot + Path : : separator ( ) + FIFO_PATH ) ;
2016-02-16 15:04:21 -06:00
if ( ! File ( pipePath ) . exists ( ) & & ! File ( pipePath ) . createDirectory ( ) )
2015-12-13 12:28:01 -06:00
{
2016-02-13 11:22:15 -06:00
Log : : error ( " Error: Failed to create pipe directory [ " + pipePath . toString ( ) + " ]. " ) ;
return Application : : EXIT_SOFTWARE ;
}
2016-04-03 22:40:56 -05:00
const std : : string pipeLoolwsd = Path ( pipePath , FIFO_LOOLWSD ) . toString ( ) ;
if ( mkfifo ( pipeLoolwsd . c_str ( ) , 0666 ) < 0 & & errno ! = EEXIST )
{
Log : : error ( " Error: Failed to create pipe FIFO [ " + pipeLoolwsd + " ]. " ) ;
return Application : : EXIT_SOFTWARE ;
}
2016-02-24 10:58:05 -06:00
// Open notify pipe
int pipeFlags = O_RDONLY | O_NONBLOCK ;
int notifyPipe = - 1 ;
2016-04-02 17:23:55 -05:00
const std : : string pipeNotify = Path ( pipePath , FIFO_ADMIN_NOTIFY ) . toString ( ) ;
2016-02-24 10:58:05 -06:00
if ( mkfifo ( pipeNotify . c_str ( ) , 0666 ) < 0 & & errno ! = EEXIST )
{
2016-04-02 17:23:55 -05:00
Log : : error ( " Error: Failed to create pipe FIFO [ " + FIFO_ADMIN_NOTIFY + " ]. " ) ;
2016-02-24 10:58:05 -06:00
exit ( Application : : EXIT_SOFTWARE ) ;
}
if ( ( notifyPipe = open ( pipeNotify . c_str ( ) , pipeFlags ) ) < 0 )
{
Log : : error ( " Error: pipe opened for reading. " ) ;
exit ( Application : : EXIT_SOFTWARE ) ;
}
if ( ( pipeFlags = fcntl ( notifyPipe , F_GETFL , 0 ) ) < 0 )
{
2016-04-02 17:23:55 -05:00
Log : : error ( " Error: failed to get pipe flags [ " + FIFO_ADMIN_NOTIFY + " ]. " ) ;
2016-02-24 10:58:05 -06:00
exit ( Application : : EXIT_SOFTWARE ) ;
}
pipeFlags & = ~ O_NONBLOCK ;
if ( fcntl ( notifyPipe , F_SETFL , pipeFlags ) < 0 )
{
2016-04-02 17:23:55 -05:00
Log : : error ( " Error: failed to set pipe flags [ " + FIFO_ADMIN_NOTIFY + " ]. " ) ;
2016-02-24 10:58:05 -06:00
exit ( Application : : EXIT_SOFTWARE ) ;
}
2016-02-24 01:43:58 -06:00
const Process : : PID brokerPid = createBroker ( ) ;
if ( brokerPid < 0 )
2016-01-06 21:17:30 -06:00
{
Log : : error ( " Failed to spawn loolBroker. " ) ;
2016-02-01 19:51:42 -06:00
return Application : : EXIT_SOFTWARE ;
2016-01-06 21:17:30 -06:00
}
2015-07-13 09:13:06 -05:00
2016-03-20 05:59:32 -05:00
// Init the Admin manager
Admin admin ( brokerPid , notifyPipe ) ;
// Init the file server
FileServer fileServer ;
2016-01-31 21:34:18 -06:00
// Configure the Server.
// Note: TCPServer internally uses the default
// ThreadPool to dispatch connections.
// The capacity of the default ThreadPool
// is increased to match MaxThreads.
// We must have sufficient available threads
// in the default ThreadPool to dispatch
// connections, otherwise we will deadlock.
2016-02-01 19:47:40 -06:00
auto params1 = new HTTPServerParams ( ) ;
params1 - > setMaxThreads ( MAX_SESSIONS ) ;
auto params2 = new HTTPServerParams ( ) ;
params2 - > setMaxThreads ( MAX_SESSIONS ) ;
2016-01-31 21:34:18 -06:00
2015-07-17 13:02:25 -05:00
// Start a server listening on the port for clients
2016-03-21 03:37:39 -05:00
SecureServerSocket svs ( ClientPortNumber ) ;
2016-01-31 21:34:18 -06:00
ThreadPool threadPool ( NumPreSpawnedChildren * 6 , MAX_SESSIONS * 2 ) ;
2016-03-20 05:59:32 -05:00
HTTPServer srv ( new ClientRequestHandlerFactory ( admin , fileServer ) , threadPool , svs , params1 ) ;
2015-07-17 13:02:25 -05:00
srv . start ( ) ;
// And one on the port for child processes
SocketAddress addr2 ( " 127.0.0.1 " , MASTER_PORT_NUMBER ) ;
2016-01-31 21:34:18 -06:00
ServerSocket svs2 ( addr2 ) ;
2016-03-20 05:59:32 -05:00
HTTPServer srv2 ( new PrisonerRequestHandlerFactory ( ) , threadPool , svs2 , params2 ) ;
2015-07-17 13:02:25 -05:00
srv2 . start ( ) ;
2016-04-03 22:40:56 -05:00
if ( ( BrokerWritePipe = open ( pipeLoolwsd . c_str ( ) , O_WRONLY ) ) < 0 )
{
Log : : error ( " Error: failed to open pipe [ " + pipeLoolwsd + " ] write only. " ) ;
return Application : : EXIT_SOFTWARE ;
}
2016-02-24 10:58:05 -06:00
threadPool . start ( admin ) ;
2015-09-11 15:31:43 -05:00
TestInput input ( * this , svs , srv ) ;
Thread inputThread ;
2016-01-15 02:40:54 -06:00
if ( LOOLWSD : : DoTest )
2015-09-11 15:31:43 -05:00
{
inputThread . start ( input ) ;
waitForTerminationRequest ( ) ;
}
2016-03-22 13:27:38 -05:00
time_t last30SecCheck = time ( NULL ) ;
time_t lastFiveMinuteCheck = time ( NULL ) ;
2015-12-28 17:24:29 -06:00
int status = 0 ;
2016-01-25 19:07:10 -06:00
while ( ! TerminationFlag & & ! LOOLWSD : : DoTest )
2015-07-17 13:02:25 -05:00
{
2016-02-24 01:43:58 -06:00
const pid_t pid = waitpid ( brokerPid , & status , WUNTRACED | WNOHANG ) ;
2015-07-17 13:02:25 -05:00
if ( pid > 0 )
{
2016-02-24 01:43:58 -06:00
if ( brokerPid = = pid )
2015-07-17 13:02:25 -05:00
{
2016-01-14 21:31:02 -06:00
if ( WIFEXITED ( status ) )
2015-07-17 13:02:25 -05:00
{
2016-01-14 21:31:02 -06:00
Log : : info ( ) < < " Child process [ " < < pid < < " ] exited with code: "
< < WEXITSTATUS ( status ) < < " . " < < Log : : end ;
2016-01-25 19:07:10 -06:00
break ;
2015-07-17 13:02:25 -05:00
}
2016-01-14 21:31:02 -06:00
else
if ( WIFSIGNALED ( status ) )
{
std : : string fate = " died " ;
if ( WCOREDUMP ( status ) )
fate = " core-dumped " ;
Log : : error ( ) < < " Child process [ " < < pid < < " ] " < < fate
< < " with " < < Util : : signalName ( WTERMSIG ( status ) )
2016-02-03 16:08:16 -06:00
< < " signal: " < < strsignal ( WTERMSIG ( status ) )
< < Log : : end ;
2015-07-17 13:02:25 -05:00
2016-01-25 19:07:10 -06:00
break ;
2016-01-14 21:31:02 -06:00
}
else if ( WIFSTOPPED ( status ) )
{
Log : : info ( ) < < " Child process [ " < < pid < < " ] stopped with "
< < Util : : signalName ( WSTOPSIG ( status ) )
2016-02-03 16:08:16 -06:00
< < " signal: " < < strsignal ( WTERMSIG ( status ) )
< < Log : : end ;
2016-01-14 21:31:02 -06:00
}
else if ( WIFCONTINUED ( status ) )
{
Log : : info ( ) < < " Child process [ " < < pid < < " ] resumed with SIGCONT. "
< < Log : : end ;
}
else
{
Log : : warn ( ) < < " Unknown status returned by waitpid: "
< < std : : hex < < status < < " . " < < Log : : end ;
}
2015-07-17 13:02:25 -05:00
}
else
{
2016-03-22 10:58:37 -05:00
Log : : error ( " An unknown child process died, pid: " + std : : to_string ( pid ) ) ;
2015-07-17 13:02:25 -05:00
}
}
else if ( pid < 0 )
2016-02-03 15:43:04 -06:00
{
2016-01-14 21:31:02 -06:00
Log : : error ( " Error: waitpid failed. " ) ;
2016-02-03 15:43:04 -06:00
// No child processes
if ( errno = = ECHILD )
{
TerminationFlag = true ;
continue ;
}
}
2016-03-22 10:52:50 -05:00
else // pid == 0, no children have died
2015-07-24 13:10:24 -05:00
{
2016-03-22 13:27:38 -05:00
time_t now = time ( NULL ) ;
if ( now > = last30SecCheck + 30 )
{
2016-04-03 22:26:58 -05:00
Log : : trace ( " 30-second check " ) ;
2016-03-22 13:27:38 -05:00
last30SecCheck = now ;
2016-03-23 11:55:28 -05:00
std : : unique_lock < std : : mutex > docBrokersLock ( docBrokersMutex ) ;
for ( auto & brokerIt : docBrokers )
2016-03-22 13:27:38 -05:00
{
2016-03-23 11:55:28 -05:00
std : : unique_lock < std : : mutex > sessionsLock ( brokerIt . second - > _wsSessionsMutex ) ;
for ( auto & sessionIt : brokerIt . second - > _wsSessions )
2016-03-22 13:27:38 -05:00
{
2016-03-23 11:55:28 -05:00
if ( sessionIt . second - > _lastMessageTime > sessionIt . second - > _idleSaveTime & &
sessionIt . second - > _lastMessageTime < now - 30 )
{
Log : : info ( " Idle save triggered for session " + sessionIt . second - > getId ( ) ) ;
2016-04-01 10:33:08 -05:00
sessionIt . second - > getQueue ( ) - > put ( " uno .uno:Save " ) ;
2016-03-23 11:55:28 -05:00
sessionIt . second - > _idleSaveTime = now ;
}
2016-03-22 13:27:38 -05:00
}
}
}
if ( now > = lastFiveMinuteCheck + 300 )
{
2016-04-03 22:26:58 -05:00
Log : : trace ( " Five-minute check " ) ;
2016-03-22 13:27:38 -05:00
lastFiveMinuteCheck = now ;
2016-03-23 11:55:28 -05:00
std : : unique_lock < std : : mutex > docBrokersLock ( docBrokersMutex ) ;
for ( auto & brokerIt : docBrokers )
2016-03-22 13:27:38 -05:00
{
2016-03-23 11:55:28 -05:00
std : : unique_lock < std : : mutex > sessionsLock ( brokerIt . second - > _wsSessionsMutex ) ;
for ( auto & sessionIt : brokerIt . second - > _wsSessions )
2016-03-22 13:27:38 -05:00
{
2016-03-23 11:55:28 -05:00
if ( sessionIt . second - > _lastMessageTime > = sessionIt . second - > _idleSaveTime & &
sessionIt . second - > _lastMessageTime > = sessionIt . second - > _autoSaveTime )
{
Log : : info ( " Auto-save triggered for session " + sessionIt . second - > getId ( ) ) ;
2016-04-01 10:33:08 -05:00
sessionIt . second - > getQueue ( ) - > put ( " uno .uno:Save " ) ;
2016-03-23 11:55:28 -05:00
sessionIt . second - > _autoSaveTime = now ;
}
2016-03-22 13:27:38 -05:00
}
}
}
2015-07-24 14:17:46 -05:00
sleep ( MAINTENANCE_INTERVAL * 2 ) ;
2015-07-24 13:10:24 -05:00
}
2015-07-17 13:02:25 -05:00
}
2015-04-16 11:15:40 -05:00
2016-01-15 02:40:54 -06:00
if ( LOOLWSD : : DoTest )
2015-09-11 15:31:43 -05:00
inputThread . join ( ) ;
2015-11-25 20:59:24 -06:00
// stop the service, no more request
srv . stop ( ) ;
srv2 . stop ( ) ;
2015-12-02 18:15:39 -06:00
2015-11-25 20:59:24 -06:00
// close all websockets
threadPool . joinAll ( ) ;
2015-12-19 19:09:48 -06:00
// Terminate child processes
2016-04-03 22:40:56 -05:00
IoUtil : : writeFIFO ( LOOLWSD : : BrokerWritePipe , " eof \n " ) ;
2016-02-24 01:43:58 -06:00
Log : : info ( " Requesting child process " + std : : to_string ( brokerPid ) + " to terminate " ) ;
Util : : requestTermination ( brokerPid ) ;
2015-12-02 18:15:39 -06:00
2015-11-25 20:59:24 -06:00
// wait broker process finish
2016-02-24 01:43:58 -06:00
waitpid ( brokerPid , & status , WUNTRACED ) ;
2015-03-17 18:56:15 -05:00
2016-04-03 22:40:56 -05:00
close ( BrokerWritePipe ) ;
2016-01-15 02:40:54 -06:00
Log : : info ( " Cleaning up childroot directory [ " + ChildRoot + " ]. " ) ;
2016-01-04 15:15:02 -06:00
std : : vector < std : : string > jails ;
2016-01-15 02:40:54 -06:00
File ( ChildRoot ) . list ( jails ) ;
2016-01-04 15:15:02 -06:00
for ( auto & jail : jails )
{
2016-01-15 02:40:54 -06:00
const auto path = ChildRoot + jail ;
2016-01-04 15:15:02 -06:00
Log : : info ( " Removing jail [ " + path + " ]. " ) ;
Util : : removeFile ( path , true ) ;
}
2015-12-28 17:24:29 -06:00
2016-03-21 03:37:39 -05:00
Poco : : Net : : uninitializeSSL ( ) ;
Poco : : Crypto : : uninitializeCrypto ( ) ;
2015-12-28 22:39:25 -06:00
Log : : info ( " Process [loolwsd] finished. " ) ;
2015-11-25 21:17:08 -06:00
return Application : : EXIT_OK ;
2015-03-17 18:56:15 -05:00
}
2015-03-04 17:14:04 -06:00
2015-03-09 10:34:11 -05:00
POCO_SERVER_MAIN ( LOOLWSD )
2015-03-04 17:14:04 -06:00
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */