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/.
*/
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>
2016-04-04 01:36:27 -05:00
# include <sys/stat.h>
# include <sys/wait.h>
2015-04-16 11:15:40 -05:00
2016-03-22 13:27:38 -05:00
# include <time.h>
2015-07-13 09:13:06 -05:00
2015-04-16 11:15:40 -05:00
# include <cassert>
2016-04-10 01:37:51 -05:00
# include <condition_variable>
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-04-14 10:22:19 -05:00
# include <thread>
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/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/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 "Common.hpp"
2016-04-16 11:56:23 -05:00
# include "Exceptions.hpp"
2016-03-20 05:59:32 -05:00
# include "FileServer.hpp"
2016-04-16 11:56:23 -05:00
# include "IoUtil.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-04-16 11:56:23 -05:00
# include "UserMessages.hpp"
2015-03-17 18:56:15 -05:00
# include "Util.hpp"
2016-04-05 11:41:10 -05:00
# include "Unit.hpp"
2016-04-07 15:59:27 -05:00
# include "UnitHTTP.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 : : 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 ;
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-14 12:04:19 -05:00
int ClientPortNumber = DEFAULT_CLIENT_PORT_NUMBER ;
2016-05-04 06:06:34 -05:00
int MasterPortNumber = DEFAULT_MASTER_PORT_NUMBER ;
2016-04-14 12:04:19 -05:00
2016-04-03 09:33:35 -05:00
/// New LOK child processes ready to host documents.
2016-04-04 21:40:18 -05:00
//TODO: Move to a more sensible namespace.
2016-04-15 09:07:24 -05:00
static bool DisplayVersion = false ;
2016-04-16 14:44:53 -05:00
static bool NoCapsForKit = false ;
2016-04-05 06:23:02 -05:00
static std : : vector < std : : shared_ptr < ChildProcess > > newChildren ;
static std : : mutex newChildrenMutex ;
static std : : condition_variable newChildrenCV ;
2016-05-07 11:16:09 -05:00
static std : : chrono : : steady_clock : : time_point lastForkRequestTime ;
2016-03-22 11:47:05 -05:00
static std : : map < std : : string , std : : shared_ptr < DocumentBroker > > docBrokers ;
static std : : mutex docBrokersMutex ;
2016-04-09 10:54:22 -05:00
// Sessions to pre-spawned child processes that have connected but are not yet assigned a
// document to work on.
static std : : mutex AvailableChildSessionMutex ;
static std : : condition_variable AvailableChildSessionCV ;
static std : : map < std : : string , std : : shared_ptr < MasterProcessSession > > AvailableChildSessions ;
2016-04-12 05:48:42 -05:00
# if ENABLE_DEBUG
static int careerSpanSeconds = 0 ;
# endif
2016-01-06 23:16:47 -06:00
2016-04-09 15:53:33 -05:00
static void forkChildren ( const int number )
2016-04-04 09:17:03 -05:00
{
2016-05-02 18:17:46 -05:00
Util : : assertIsLocked ( newChildrenMutex ) ;
2016-04-04 09:17:03 -05:00
2016-04-09 15:53:33 -05:00
if ( number > 0 )
{
const std : : string aMessage = " spawn " + std : : to_string ( number ) + " \n " ;
Log : : debug ( " MasterToForKit: " + aMessage . substr ( 0 , aMessage . length ( ) - 1 ) ) ;
IoUtil : : writeFIFO ( LOOLWSD : : ForKitWritePipe , aMessage ) ;
2016-05-07 11:16:09 -05:00
lastForkRequestTime = std : : chrono : : steady_clock : : now ( ) ;
2016-04-09 15:53:33 -05:00
}
2016-04-04 09:17:03 -05:00
}
2016-05-02 21:57:27 -05:00
/// Called on startup only.
2016-04-08 03:00:58 -05:00
static void preForkChildren ( )
2016-04-04 09:17:03 -05:00
{
2016-04-05 06:23:02 -05:00
std : : unique_lock < std : : mutex > lock ( newChildrenMutex ) ;
2016-04-06 13:50:55 -05:00
int numPreSpawn = LOOLWSD : : NumPreSpawnedChildren ;
2016-04-09 11:30:48 -05:00
UnitWSD : : get ( ) . preSpawnCount ( numPreSpawn ) ;
2016-04-09 15:53:33 -05:00
- - numPreSpawn ; // ForKit always spawns one child at startup.
2016-04-06 13:50:55 -05:00
forkChildren ( numPreSpawn ) ;
2016-04-04 09:17:03 -05:00
}
2016-05-02 21:57:27 -05:00
static void prespawnChildren ( )
{
2016-05-03 07:36:53 -05:00
std : : unique_lock < std : : mutex > lock ( newChildrenMutex , std : : defer_lock ) ;
if ( ! lock . try_lock ( ) )
{
// We are forking already? Try later.
return ;
}
2016-05-02 21:57:27 -05:00
2016-05-07 11:16:09 -05:00
const auto duration = ( std : : chrono : : steady_clock : : now ( ) - lastForkRequestTime ) ;
if ( std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( duration ) . count ( ) < = CHILD_TIMEOUT_SECS * 1000 )
{
// Not enough time passed to balance children.
return ;
}
2016-05-02 21:57:27 -05:00
for ( int i = newChildren . size ( ) - 1 ; i > = 0 ; - - i )
{
if ( ! newChildren [ i ] - > isAlive ( ) )
{
newChildren . erase ( newChildren . begin ( ) + i ) ;
}
}
const int available = newChildren . size ( ) ;
int balance = LOOLWSD : : NumPreSpawnedChildren ;
balance - = available ;
forkChildren ( balance ) ;
}
2016-05-07 08:42:10 -05:00
static size_t addNewChild ( const std : : shared_ptr < ChildProcess > & child )
{
std : : unique_lock < std : : mutex > lock ( newChildrenMutex ) ;
newChildren . emplace_back ( child ) ;
const auto count = newChildren . size ( ) ;
Log : : info ( ) < < " Have " < < count < < " "
< < ( count = = 1 ? " child " : " children " )
< < " . " < < Log : : end ;
newChildrenCV . notify_one ( ) ;
return count ;
}
2016-04-08 03:00:58 -05:00
static std : : shared_ptr < ChildProcess > getNewChild ( )
2016-04-03 18:00:37 -05:00
{
2016-04-05 06:23:02 -05:00
std : : unique_lock < std : : mutex > lock ( newChildrenMutex ) ;
2016-04-03 18:00:37 -05:00
2016-04-24 10:56:02 -05:00
namespace chrono = std : : chrono ;
const auto startTime = chrono : : steady_clock : : now ( ) ;
do
2016-04-03 18:00:37 -05:00
{
2016-04-24 10:56:02 -05:00
const int available = newChildren . size ( ) ;
int balance = LOOLWSD : : NumPreSpawnedChildren ;
if ( available = = 0 )
{
Log : : error ( " getNewChild: No available child. Sending spawn request to forkit and failing. " ) ;
}
else
{
balance - = available - 1 ; // Minus the one we'll dispatch just now.
2016-05-03 06:27:46 -05:00
balance = std : : max ( balance , 0 ) ;
2016-04-24 10:56:02 -05:00
}
2016-04-03 18:00:37 -05:00
2016-04-24 10:56:02 -05:00
Log : : debug ( " getNewChild: Have " + std : : to_string ( available ) + " children, forking " + std : : to_string ( balance ) ) ;
forkChildren ( balance ) ;
2016-04-03 18:00:37 -05:00
2016-04-24 10:56:02 -05:00
const auto timeout = chrono : : milliseconds ( CHILD_TIMEOUT_SECS * 1000 ) ;
if ( newChildrenCV . wait_for ( lock , timeout , [ ] ( ) { return ! newChildren . empty ( ) ; } ) )
{
auto child = newChildren . back ( ) ;
newChildren . pop_back ( ) ;
// Validate before returning.
if ( child & & child - > isAlive ( ) )
{
Log : : debug ( " getNewChild: Returning new child [ " + std : : to_string ( child - > getPid ( ) ) + " ]. " ) ;
return child ;
}
Log : : debug ( " getNewChild: No live child, forking more. " ) ;
}
2016-04-03 18:00:37 -05:00
}
2016-04-24 10:56:02 -05:00
while ( chrono : : duration_cast < chrono : : milliseconds > ( chrono : : steady_clock : : now ( ) - startTime ) . count ( ) < CHILD_TIMEOUT_SECS * 2000 ) ;
2016-04-03 18:00:37 -05:00
2016-04-24 10:56:02 -05:00
Log : : debug ( " getNewChild: Timed out while waiting for new child. " ) ;
2016-04-03 18:00:37 -05:00
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-04-18 07:33:22 -05:00
Path tempPath = Path : : forDirectory ( TemporaryFile ( ) . tempName ( ) + " / " ) ;
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 :
2016-04-24 21:09:13 -05:00
static void waitBridgeCompleted ( const std : : shared_ptr < MasterProcessSession > & session )
2016-04-09 10:54:22 -05:00
{
bool isFound = false ;
std : : unique_lock < std : : mutex > lock ( AvailableChildSessionMutex ) ;
2016-04-24 21:09:13 -05:00
Log : : debug ( ) < < " Waiting for client session [ " < < session - > getId ( ) < < " ] to connect. " < < Log : : end ;
AvailableChildSessionCV . wait_for (
lock ,
std : : chrono : : milliseconds ( COMMAND_TIMEOUT_MS ) ,
[ & isFound , & session ]
{
return ( isFound = AvailableChildSessions . find ( session - > getId ( ) ) ! = AvailableChildSessions . end ( ) ) ;
} ) ;
2016-04-09 10:54:22 -05:00
2016-04-24 21:09:13 -05:00
if ( ! isFound )
2016-04-09 10:54:22 -05:00
{
2016-04-24 21:09:13 -05:00
// Let the client know we can't serve now.
Log : : error ( session - > getName ( ) + " : Failed to connect to lokit process. Client cannot serve now. " ) ;
throw WebSocketErrorMessageException ( SERVICE_UNAVALABLE_INTERNAL_ERROR ) ;
2016-04-09 10:54:22 -05:00
}
2016-04-24 21:09:13 -05:00
Log : : debug ( " Waiting child session permission, done! " ) ;
AvailableChildSessions . erase ( session - > getId ( ) ) ;
2016-04-09 10:54:22 -05:00
}
2016-04-16 11:56:23 -05:00
/// Handle POST requests.
/// Always throw on error, do not set response status here.
/// Returns true if a response has been sent.
static bool 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.
2016-04-16 11:56:23 -05:00
throw std : : runtime_error ( " Failed to spawn lokit child. " ) ;
2016-04-03 20:40:14 -05:00
}
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
2016-04-16 11:56:23 -05:00
//FIXME: What if the same document is already open? Need a fake dockey here?
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-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-04-24 21:09:13 -05:00
// Request the child to connect to us and add this session.
2016-04-16 16:18:51 -05:00
auto sessionsCount = docBroker - > addSession ( session ) ;
2016-04-24 21:09:13 -05:00
Log : : trace ( docKey + " , ws_sessions++: " + std : : to_string ( sessionsCount ) ) ;
2016-03-13 13:00:19 -05:00
lock . unlock ( ) ;
2016-04-16 16:18:51 -05:00
Log : : trace ( docKey + " , ws_sessions++: " + std : : to_string ( sessionsCount ) ) ;
2016-03-13 13:00:19 -05:00
2016-04-24 21:09:13 -05:00
// Wait until the client has connected with a prison socket.
waitBridgeCompleted ( session ) ;
2016-04-10 21:16:04 -05:00
// Now the bridge between the client and kit processes is connected
2016-04-09 10:54:22 -05:00
// Let messages flow
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 ( ) ;
2016-04-16 16:18:51 -05:00
sessionsCount = docBroker - > removeSession ( id ) ;
if ( sessionsCount = = 0 )
2016-03-13 13:00:19 -05:00
{
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-04-16 11:56:23 -05:00
//TODO: We should differentiate between bad request and failed conversion.
throw BadRequestException ( " Failed to convert and send file. " ) ;
2016-01-24 13:48:09 -06:00
}
2016-04-16 11:56:23 -05:00
return true ;
2016-01-24 13:48:09 -06:00
}
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-04-16 11:56:23 -05:00
if ( form . has ( " childid " ) & & form . has ( " name " ) )
2016-01-24 13:48:09 -06:00
{
2016-04-16 11:56:23 -05:00
const std : : string formChildid ( form . get ( " childid " ) ) ;
const std : : string formName ( form . get ( " name " ) ) ;
// protect against attempts to inject something funny here
if ( formChildid . find ( ' / ' ) = = std : : string : : npos & & formName . find ( ' / ' ) = = std : : string : : npos )
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 ( ) ;
2016-04-18 07:33:22 -05:00
std : : string fileName = dirPath + " / " + form . get ( " name " ) ;
2016-01-24 13:48:09 -06:00
File ( tmpPath ) . moveTo ( fileName ) ;
2016-04-16 11:56:23 -05:00
return false ;
2015-10-22 10:27:29 -05:00
}
}
2016-01-24 13:48:09 -06:00
}
else if ( tokens . count ( ) > = 4 )
{
Log : : info ( " File download request. " ) ;
// The user might request a file to download
2016-04-16 11:56:23 -05:00
//TODO: Check that the user in question has access to this file!
2016-01-24 13:48:09 -06:00
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 ) ;
2016-04-18 07:33:22 -05:00
const std : : string filePath = dirPath + " / " + fileName ;
2016-01-24 13:48:09 -06:00
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-04-16 11:56:23 -05:00
const std : : string mimeType = form . has ( " mime_type " )
? form . get ( " mime_type " )
: " application/octet-stream " ;
2016-01-24 13:48:09 -06:00
response . sendFile ( filePath , mimeType ) ;
2016-04-16 11:56:23 -05:00
//TODO: Cleanup on error.
2016-01-24 13:48:09 -06:00
Util : : removeFile ( dirPath , true ) ;
2016-04-16 11:56:23 -05:00
return true ;
2015-10-09 07:55:49 -05:00
}
2015-03-04 17:14:04 -06:00
}
2016-04-16 11:56:23 -05:00
throw BadRequestException ( " Invalid or unknown request. " ) ;
2016-01-24 13:48:09 -06:00
}
2015-04-21 07:06:41 -05:00
2016-04-16 11:56:23 -05:00
/// Handle GET requests.
static void handleGetRequest ( HTTPServerRequest & request , std : : shared_ptr < WebSocket > & ws , const std : : string & id )
2016-01-24 13:48:09 -06:00
{
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-04-24 10:08:08 -05:00
// indicator to the client that document broker is searching
std : : string status ( " statusindicator: find " ) ;
2016-04-26 22:44:23 -05:00
Log : : trace ( " Sending to Client [ " + status + " ]. " ) ;
2016-04-24 10:08:08 -05:00
ws - > sendFrame ( status . data ( ) , ( int ) status . size ( ) ) ;
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-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 ) ;
2016-04-24 11:36:27 -05:00
}
docBrokersLock . unlock ( ) ;
2016-04-17 22:29:03 -05:00
2016-04-24 11:36:27 -05:00
if ( docBroker )
{
2016-04-17 22:29:03 -05:00
// If this document is going out, wait.
if ( docBroker - > isMarkedToDestroy ( ) )
{
2016-04-24 11:36:27 -05:00
Log : : debug ( " Document [ " + docKey + " ] is marked to destroy, waiting to reload. " ) ;
2016-04-17 22:29:03 -05:00
const auto timeout = POLL_TIMEOUT_MS / 2 ;
for ( size_t i = 0 ; i < COMMAND_TIMEOUT_MS / timeout ; + + i )
{
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( timeout ) ) ;
2016-04-24 11:36:27 -05:00
std : : unique_lock < std : : mutex > lock ( docBrokersMutex ) ;
2016-04-17 22:29:03 -05:00
if ( docBrokers . find ( docKey ) = = docBrokers . end ( ) )
{
docBroker . reset ( ) ;
break ;
}
}
if ( docBroker )
{
// Still here, but marked to destroy.
2016-04-25 04:21:54 -05:00
Log : : error ( " Timed out while waiting for document to unload before loading. Service Unavailable. " ) ;
2016-04-24 11:36:27 -05:00
throw WebSocketErrorMessageException ( SERVICE_UNAVALABLE_INTERNAL_ERROR ) ;
2016-04-17 22:29:03 -05:00
}
}
2016-03-13 12:21:36 -05:00
}
2016-04-17 22:29:03 -05:00
2016-04-24 11:36:27 -05:00
bool newDoc = false ;
2016-04-17 22:29:03 -05:00
if ( ! docBroker )
2016-03-13 12:21:36 -05:00
{
2016-04-24 11:36:27 -05:00
newDoc = true ;
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.
2016-04-24 11:36:27 -05:00
Log : : error ( " Failed to get new child. Service Unavailable. " ) ;
2016-04-17 07:42:07 -05:00
throw WebSocketErrorMessageException ( SERVICE_UNAVALABLE_INTERNAL_ERROR ) ;
2016-04-03 20:40:14 -05:00
}
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-05-02 06:21:30 -05:00
child - > setDocumentBroker ( docBroker ) ;
2016-04-24 11:36:27 -05:00
}
// Validate the broker.
if ( ! docBroker | | ! docBroker - > isAlive ( ) )
{
Log : : error ( " DocBroker is invalid or child had SDS. Service Unavailable. " ) ;
if ( ! newDoc )
{
// Remove.
std : : unique_lock < std : : mutex > lock ( docBrokersMutex ) ;
docBrokers . erase ( docKey ) ;
throw WebSocketErrorMessageException ( SERVICE_UNAVALABLE_INTERNAL_ERROR ) ;
}
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-24 11:36:27 -05:00
if ( newDoc )
2016-04-09 10:54:22 -05:00
{
2016-04-24 11:36:27 -05:00
std : : unique_lock < std : : mutex > lock ( docBrokersMutex ) ;
docBrokers . emplace ( docKey , docBroker ) ;
2016-04-09 10:54:22 -05:00
}
2016-04-16 11:56:23 -05:00
2016-04-24 11:36:27 -05:00
// Above this point exceptions are safe and will auto-cleanup.
// Below this, we need to cleanup internal references.
std : : shared_ptr < MasterProcessSession > session ;
try
{
// For ToClient sessions, we store incoming messages in a queue and have a separate
// thread to pump them. This is to empty the queue when we get a "canceltiles" message.
auto queue = std : : make_shared < BasicTileQueue > ( ) ;
session = std : : make_shared < MasterProcessSession > ( id , LOOLSession : : Kind : : ToClient , ws , docBroker , queue ) ;
2016-04-24 21:09:13 -05:00
// Request the child to connect to us and add this session.
2016-04-24 11:36:27 -05:00
const auto sessionsCount = docBroker - > addSession ( session ) ;
Log : : trace ( docKey + " , ws_sessions++: " + std : : to_string ( sessionsCount ) ) ;
// indicator to a client that is waiting to connect to lokit process
status = " statusindicator: connect " ;
2016-04-26 22:44:23 -05:00
Log : : trace ( " Sending to Client [ " + status + " ]. " ) ;
2016-04-24 11:36:27 -05:00
ws - > sendFrame ( status . data ( ) , ( int ) status . size ( ) ) ;
2016-04-24 21:09:13 -05:00
// Wait until the client has connected with a prison socket.
waitBridgeCompleted ( session ) ;
2016-04-24 11:36:27 -05:00
// Now the bridge beetween the client and kit process is connected
// Let messages flow
2016-04-24 21:09:13 -05:00
2016-04-24 11:36:27 -05:00
status = " statusindicator: ready " ;
2016-04-26 22:44:23 -05:00
Log : : trace ( " Sending to Client [ " + status + " ]. " ) ;
2016-04-24 11:36:27 -05:00
ws - > sendFrame ( status . data ( ) , ( int ) status . size ( ) ) ;
2016-01-05 20:29:12 -06:00
2016-04-24 11:36:27 -05:00
QueueHandler handler ( queue , session , " wsd_queue_ " + session - > getId ( ) ) ;
Thread queueHandlerThread ;
queueHandlerThread . start ( handler ) ;
2016-01-24 13:48:09 -06:00
2016-04-24 11:36:27 -05:00
IoUtil : : SocketProcessor ( ws ,
[ & queue ] ( const std : : vector < char > & payload )
{
queue - > put ( payload ) ;
return true ;
} ,
[ & session ] ( ) { session - > closeFrame ( ) ; } ,
2016-05-02 18:53:13 -05:00
[ & queueHandlerThread ] ( ) { return TerminationFlag | | ! queueHandlerThread . isRunning ( ) ; } ) ;
2016-04-24 11:36:27 -05:00
2016-05-08 09:07:17 -05:00
if ( ! session - > _bLoadError )
2016-01-24 13:48:09 -06:00
{
2016-05-08 09:07:17 -05:00
// If we are the last, we must wait for the save to complete.
const bool canDestroy = docBroker - > canDestroy ( ) ;
if ( canDestroy )
{
Log : : info ( " Shutdown of the last session, saving the document before tearing down. " ) ;
}
2016-04-24 11:36:27 -05:00
// Use auto-save to save only when there are modifications since last save.
// We also need to wait until the save notification reaches us
// and Storage persists the document.
2016-05-08 09:07:17 -05:00
if ( ! docBroker - > autoSave ( canDestroy , COMMAND_TIMEOUT_MS ) )
2016-04-24 11:36:27 -05:00
{
Log : : error ( " Auto-save before closing failed. " ) ;
}
}
else
2016-04-10 21:07:09 -05:00
{
2016-04-24 11:36:27 -05:00
Log : : info ( " Clearing the queue. " ) ;
queue - > clear ( ) ;
2016-04-10 21:07:09 -05:00
}
2016-04-24 11:36:27 -05:00
Log : : info ( " Finishing GET request handler for session [ " + id + " ]. Joining the queue. " ) ;
queue - > put ( " eof " ) ;
queueHandlerThread . join ( ) ;
2016-03-14 10:53:31 -05:00
}
2016-04-24 11:36:27 -05:00
catch ( const std : : exception & exc )
2016-03-14 10:53:31 -05:00
{
2016-04-24 11:36:27 -05:00
Log : : error ( " Error in client request handler: " + std : : string ( exc . what ( ) ) ) ;
2016-03-14 10:53:31 -05:00
}
2016-03-22 13:27:38 -05:00
docBrokersLock . lock ( ) ;
2016-04-24 11:36:27 -05:00
const auto sessionsCount = docBroker - > removeSession ( id ) ;
2016-04-16 16:18:51 -05:00
Log : : trace ( docKey + " , ws_sessions--: " + std : : to_string ( sessionsCount ) ) ;
if ( sessionsCount = = 0 )
2016-03-13 09:04:54 -05:00
{
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-04-17 21:32:58 -05:00
Log : : info ( " Removing complete doc [ " + docKey + " ] from Admin. " ) ;
Admin : : instance ( ) . rmDoc ( docKey ) ;
2016-03-13 09:04:54 -05:00
}
2016-04-18 18:12:26 -05:00
docBrokersLock . unlock ( ) ;
if ( session - > isCloseFrame ( ) )
{
Log : : trace ( " Normal close handshake. " ) ;
if ( session - > shutdownPeer ( WebSocket : : WS_NORMAL_CLOSE , " " ) )
{
// Client initiated close handshake
// respond close frame
ws - > shutdown ( ) ;
}
}
else
{
// something wrong, with internal exceptions
Log : : trace ( " Abnormal close handshake. " ) ;
session - > closeFrame ( ) ;
ws - > shutdown ( WebSocket : : WS_ENDPOINT_GOING_AWAY , SERVICE_UNAVALABLE_INTERNAL_ERROR ) ;
session - > shutdownPeer ( WebSocket : : WS_ENDPOINT_GOING_AWAY , SERVICE_UNAVALABLE_INTERNAL_ERROR ) ;
}
2016-01-24 13:48:09 -06:00
}
2016-04-16 11:56:23 -05:00
/// Sends back the WOPI Discovery XML.
/// The XML needs to be preprocessed to stamp the correct URL etc.
/// Returns true if a response has been sent.
static bool handleGetWOPIDiscovery ( HTTPServerRequest & request , HTTPServerResponse & response )
2016-03-15 10:35:59 -05:00
{
2016-04-14 07:43:13 -05:00
std : : string discoveryPath = Path ( Application : : instance ( ) . commandPath ( ) ) . parent ( ) . toString ( ) + " discovery.xml " ;
if ( ! File ( discoveryPath ) . exists ( ) )
{
discoveryPath = LOOLWSD_DATADIR " /discovery.xml " ;
}
2016-04-16 07:10:52 -05:00
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-16 07:10:52 -05:00
const std : : string uriValue = ( LOOLWSD : : SSLEnabled ? " https:// " : " http:// " ) +
( LOOLWSD : : ServerName . empty ( ) ? request . getHost ( ) : LOOLWSD : : ServerName ) +
2016-04-20 05:50:14 -05:00
" /loleaflet/ " LOOLWSD_VERSION " /loleaflet.html? " ;
2016-03-15 10:35:59 -05:00
InputSource inputSrc ( discoveryPath ) ;
2016-04-16 07:10:52 -05:00
DOMParser parser ;
2016-03-15 10:35:59 -05:00
AutoPtr < Poco : : XML : : Document > docXML = parser . parse ( & inputSrc ) ;
AutoPtr < NodeList > listNodes = docXML - > getElementsByTagName ( action ) ;
2016-04-16 07:10:52 -05:00
for ( unsigned long it = 0 ; it < listNodes - > length ( ) ; + + it )
2016-03-15 10:35:59 -05:00
{
static_cast < Element * > ( listNodes - > item ( it ) ) - > setAttribute ( urlsrc , uriValue ) ;
}
std : : ostringstream ostrXML ;
2016-04-16 07:10:52 -05:00
DOMWriter writer ;
2016-03-15 10:35:59 -05:00
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-04-16 07:10:52 -05:00
Log : : debug ( " Sent discovery.xml successfully. " ) ;
2016-04-16 11:56:23 -05:00
return true ;
2016-03-15 10:35:59 -05:00
}
2016-01-24 13:48:09 -06:00
public :
void handleRequest ( HTTPServerRequest & request , HTTPServerResponse & response ) override
2016-04-07 15:59:27 -05:00
{
2016-04-12 15:30:06 -05:00
if ( UnitWSD : : get ( ) . filterHandleRequest (
UnitWSD : : TestRequest : : TEST_REQ_CLIENT ,
request , response ) )
return ;
2016-04-07 15:59:27 -05:00
handleClientRequest ( request , response ) ;
}
static void handleClientRequest ( HTTPServerRequest & request , HTTPServerResponse & response )
2016-01-24 13:48:09 -06:00
{
const auto id = LOOLWSD : : GenSessionId ( ) ;
2016-04-07 11:31:56 -05:00
Util : : setThreadName ( " client_ws_ " + id ) ;
2016-02-29 06:25:12 -06:00
2016-04-07 07:51:49 -05:00
Log : : debug ( " Thread started. " ) ;
2016-01-24 13:48:09 -06:00
2016-04-16 11:56:23 -05:00
bool responded = false ;
2016-01-24 13:48:09 -06:00
try
{
2016-03-15 10:35:59 -05:00
if ( request . getMethod ( ) = = HTTPRequest : : HTTP_GET & & request . getURI ( ) = = " /hosting/discovery " )
{
// http://server/hosting/discovery
2016-04-16 11:56:23 -05:00
responded = handleGetWOPIDiscovery ( 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
{
2016-04-16 11:56:23 -05:00
responded = handlePostRequest ( request , response , id ) ;
2016-01-24 13:48:09 -06:00
}
else
{
2016-04-16 11:56:23 -05:00
auto ws = std : : make_shared < WebSocket > ( request , response ) ;
try
{
responded = true ; // After upgrading to WS we should not set HTTP response.
handleGetRequest ( request , ws , id ) ;
}
catch ( const WebSocketErrorMessageException & exc )
{
2016-04-24 10:08:08 -05:00
// Internal error that should be passed on to the client.
2016-04-16 11:56:23 -05:00
Log : : error ( std : : string ( " ClientRequestHandler::handleRequest: WebSocketErrorMessageException: " ) + exc . what ( ) ) ;
try
{
const std : : string msg = std : : string ( " error: " ) + exc . what ( ) ;
ws - > sendFrame ( msg . data ( ) , msg . size ( ) ) ;
2016-04-17 07:42:07 -05:00
// abnormal close frame handshake
ws - > shutdown ( WebSocket : : WS_ENDPOINT_GOING_AWAY , exc . what ( ) ) ;
2016-04-16 11:56:23 -05:00
}
catch ( const std : : exception & exc2 )
{
Log : : error ( std : : string ( " ClientRequestHandler::handleRequest: exception while sending WS error message: " ) + exc2 . what ( ) ) ;
}
}
2016-01-24 13:48:09 -06:00
}
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-04-16 11:56:23 -05:00
Log : : error ( ) < < " ClientRequestHandler::handleRequest: PocoException: " < < exc . displayText ( )
2016-01-06 08:13:21 -06:00
< < ( exc . nested ( ) ? " ( " + exc . nested ( ) - > displayText ( ) + " ) " : " " )
< < Log : : end ;
2016-04-16 11:56:23 -05:00
response . setStatusAndReason ( HTTPResponse : : HTTP_SERVICE_UNAVAILABLE ) ;
}
catch ( const UnauthorizedRequestException & exc )
{
Log : : error ( std : : string ( " ClientRequestHandler::handleRequest: UnauthorizedException: " ) + exc . what ( ) ) ;
response . setStatusAndReason ( HTTPResponse : : HTTP_UNAUTHORIZED ) ;
}
catch ( const BadRequestException & exc )
{
Log : : error ( std : : string ( " ClientRequestHandler::handleRequest: BadRequestException: " ) + exc . what ( ) ) ;
response . setStatusAndReason ( HTTPResponse : : HTTP_BAD_REQUEST ) ;
2016-01-06 08:13:21 -06:00
}
catch ( const std : : exception & exc )
{
2016-03-07 10:22:39 -06:00
Log : : error ( std : : string ( " ClientRequestHandler::handleRequest: Exception: " ) + exc . what ( ) ) ;
2016-04-16 11:56:23 -05:00
response . setStatusAndReason ( HTTPResponse : : HTTP_SERVICE_UNAVAILABLE ) ;
2016-01-06 08:13:21 -06:00
}
2016-04-18 18:12:26 -05:00
catch ( . . . )
{
Log : : error ( " ClientRequestHandler::handleRequest:: Unexpected exception " ) ;
}
2016-04-16 11:56:23 -05:00
if ( ! responded )
2016-01-06 08:13:21 -06:00
{
2016-04-16 11:56:23 -05:00
response . setContentLength ( 0 ) ;
response . send ( ) ;
2016-01-05 20:29:12 -06:00
}
2015-04-21 07:06:41 -05:00
2016-04-07 07:51:49 -05:00
Log : : debug ( " Thread finished. " ) ;
2016-01-05 20:29:12 -06:00
}
} ;
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-07 15:59:27 -05:00
{
2016-04-12 15:30:06 -05:00
if ( UnitWSD : : get ( ) . filterHandleRequest (
UnitWSD : : TestRequest : : TEST_REQ_PRISONER ,
request , response ) )
return ;
2016-04-07 15:59:27 -05:00
handlePrisonerRequest ( request , response ) ;
}
static void handlePrisonerRequest ( HTTPServerRequest & request , HTTPServerResponse & response )
2016-01-05 20:29:12 -06:00
{
2016-04-07 11:31:56 -05:00
Util : : setThreadName ( " prison_ws " ) ;
2016-04-03 09:31:46 -05:00
Log : : debug ( " Child connection with URI [ " + request . getURI ( ) + " ]. " ) ;
2016-05-04 06:06:34 -05:00
assert ( request . serverAddress ( ) . port ( ) = = MasterPortNumber ) ;
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 ) ;
2016-04-12 15:30:06 -05:00
UnitWSD : : get ( ) . newChild ( ws ) ;
2016-05-07 08:42:10 -05:00
addNewChild ( std : : make_shared < ChildProcess > ( pid , ws ) ) ;
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-04-06 07:43:44 -05:00
std : : string sessionId ;
std : : string jailId ;
2016-04-15 04:00:22 -05:00
std : : string docKey ;
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 ( ) ;
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-04-07 11:31:56 -05:00
Util : : setThreadName ( " prison_ws_ " + sessionId ) ;
2016-04-07 02:55:57 -05:00
// Misleading debug message, we obviously started already a while ago and have done lots
// of stuff already.
2016-04-07 07:51:49 -05:00
Log : : debug ( " Thread 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
2016-04-24 19:34:38 -05:00
// Jail id should be the PID, beacuse Admin need it to calculate the memory
const Poco : : Process : : PID pid = std : : stoi ( jailId ) ;
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
2016-04-24 21:09:13 -05:00
// Connect the prison session to the client.
docBroker - > connectPeers ( session ) ;
2016-04-09 10:54:22 -05:00
std : : unique_lock < std : : mutex > lock ( AvailableChildSessionMutex ) ;
AvailableChildSessions . emplace ( sessionId , session ) ;
2016-03-12 08:07:33 -06:00
Log : : info ( ) < < " mapped " < < session < < " jailId= " < < jailId < < " , id= " < < sessionId
2016-04-09 10:54:22 -05:00
< < " into _availableChildSessions, size= " < < AvailableChildSessions . size ( ) < < Log : : end ;
2016-03-12 08:07:33 -06:00
lock . unlock ( ) ;
2016-04-09 10:54:22 -05:00
AvailableChildSessionCV . notify_one ( ) ;
2016-01-05 20:29:12 -06:00
2016-04-15 04:00:22 -05:00
Log : : info ( " Adding doc " + docKey + " to Admin " ) ;
2016-04-17 21:11:10 -05:00
Admin : : instance ( ) . addDoc ( docKey , pid , docBroker - > getFilename ( ) , sessionId ) ;
2016-04-06 07:43:44 -05:00
2016-04-17 08:54:05 -05:00
UnitWSD : : get ( ) . onChildConnected ( pid , sessionId ) ;
2016-04-14 22:12:25 -05:00
IoUtil : : SocketProcessor ( ws ,
2016-04-18 18:12:26 -05:00
[ & 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
} ,
2016-04-18 18:12:26 -05:00
[ & session ] ( ) { session - > closeFrame ( ) ; } ,
2016-04-07 12:00:57 -05:00
[ ] ( ) { return TerminationFlag ; } ) ;
2016-04-18 18:12:26 -05:00
if ( session - > isCloseFrame ( ) )
{
Log : : trace ( " Normal close handshake. " ) ;
if ( session - > shutdownPeer ( WebSocket : : WS_NORMAL_CLOSE , " " ) )
{
// LOKit initiated close handshake
// respond close frame
ws - > shutdown ( ) ;
}
}
else
{
// something wrong, with internal exceptions
Log : : trace ( " Abnormal close handshake. " ) ;
session - > closeFrame ( ) ;
ws - > shutdown ( WebSocket : : WS_ENDPOINT_GOING_AWAY , SERVICE_UNAVALABLE_INTERNAL_ERROR ) ;
session - > shutdownPeer ( WebSocket : : WS_ENDPOINT_GOING_AWAY , SERVICE_UNAVALABLE_INTERNAL_ERROR ) ;
}
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
}
2016-04-18 18:12:26 -05:00
catch ( . . . )
{
Log : : error ( " PrisonerRequestHandler::handleRequest:: Unexpected exception " ) ;
}
2015-12-26 10:33:01 -06:00
2016-04-06 07:43:44 -05:00
if ( ! jailId . empty ( ) )
{
2016-04-15 04:00:22 -05:00
Log : : info ( " Removing doc " + docKey + " from Admin " ) ;
2016-04-17 21:11:10 -05:00
Admin : : instance ( ) . rmDoc ( docKey , sessionId ) ;
2016-04-06 07:43:44 -05:00
}
2016-04-07 07:51:49 -05:00
Log : : debug ( " Thread 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-04-06 07:43:44 -05:00
ClientRequestHandlerFactory ( FileServer & fileServer )
: _fileServer ( fileServer )
2016-03-20 05:59:32 -05:00
{ }
2015-03-04 17:14:04 -06:00
HTTPRequestHandler * createRequestHandler ( const HTTPServerRequest & request ) override
{
2016-04-07 11:31:56 -05:00
Util : : setThreadName ( " client_req_hdl " ) ;
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
2016-04-28 01:43:19 -05:00
for ( const auto & it : request )
2015-03-04 17:14:04 -06:00
{
2016-04-28 01:43:19 -05: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 " )
{
2016-04-06 07:43:44 -05:00
requestHandler = Admin : : createRequestHandler ( ) ;
2016-03-20 05:59:32 -05:00
}
// Client post and websocket connections
else
{
requestHandler = new ClientRequestHandler ( ) ;
}
return requestHandler ;
}
private :
FileServer & _fileServer ;
} ;
class PrisonerRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public :
HTTPRequestHandler * createRequestHandler ( const HTTPServerRequest & request ) override
{
2016-04-07 11:31:56 -05:00
Util : : setThreadName ( " prsnr_req_hdl " ) ;
2016-03-20 05:59:32 -05:00
auto logger = Log : : info ( ) ;
logger < < " Request from " < < request . clientAddress ( ) . toString ( ) < < " : "
< < request . getMethod ( ) < < " " < < request . getURI ( ) < < " "
< < request . getVersion ( ) ;
2016-04-28 01:43:19 -05:00
for ( const auto & it : request )
2016-03-20 05:59:32 -05:00
{
2016-04-28 01:43:19 -05:00
logger < < " / " < < it . first < < " : " < < it . second ;
2016-03-20 05:59:32 -05:00
}
logger < < Log : : end ;
return new PrisonerRequestHandler ( ) ;
2015-03-04 17:14:04 -06:00
}
} ;
2015-12-27 21:47:39 -06:00
std : : atomic < unsigned > LOOLWSD : : NextSessionId ;
2016-04-07 03:27:43 -05:00
int LOOLWSD : : ForKitWritePipe = - 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-04-14 04:13:30 -05:00
std : : string LOOLWSD : : ServerName ;
2016-03-20 09:07:24 -05:00
std : : string LOOLWSD : : FileServerRoot ;
2016-04-06 22:36:54 -05:00
std : : string LOOLWSD : : AdminCreds ;
2016-04-06 23:32:28 -05:00
bool LOOLWSD : : AllowLocalStorage = false ;
2016-04-13 06:08:47 -05:00
bool LOOLWSD : : SSLEnabled =
2016-04-14 05:42:12 -05:00
# if ENABLE_SSL
2016-04-13 06:08:47 -05:00
true ;
# else
false ;
# endif
2016-04-08 03:00:58 -05:00
static std : : string UnitTestLibrary ;
2015-07-13 09:13:06 -05:00
2016-04-06 22:51:58 -05:00
unsigned int LOOLWSD : : NumPreSpawnedChildren = 0 ;
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-04-16 13:37:38 -05:00
if ( geteuid ( ) = = 0 )
{
throw std : : runtime_error ( " Do not run as root. Please run as lool user. " ) ;
}
if ( ! UnitWSD : : init ( UnitWSD : : UnitType : : TYPE_WSD ,
UnitTestLibrary ) )
{
throw std : : runtime_error ( " Failed to load wsd unit test library. " ) ;
}
2016-04-06 22:36:54 -05:00
// Load default configuration files, if present.
2016-03-24 04:37:17 -05:00
if ( loadConfiguration ( ) = = 0 )
{
2016-04-06 22:36:54 -05:00
// Fallback to the default path.
const 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
2016-04-16 13:37:38 -05:00
// This overrides whatever is in the config file,
// which forces admins to set this flag on the command-line.
config ( ) . setBool ( " storage.filesystem[@allow] " , AllowLocalStorage ) ;
2016-04-06 22:36:54 -05:00
if ( ! AdminCreds . empty ( ) )
{
// Set the Admin Console credentials, if provided.
StringTokenizer tokens ( AdminCreds , " / " , StringTokenizer : : TOK_IGNORE_EMPTY | StringTokenizer : : TOK_TRIM ) ;
if ( tokens . count ( ) = = 2 )
{
config ( ) . setString ( " admin_console_username " , tokens [ 0 ] ) ;
config ( ) . setString ( " admin_console_password " , tokens [ 1 ] ) ;
}
}
2016-04-16 13:37:38 -05:00
// Allow UT to manipulate before using configuration values.
UnitWSD : : get ( ) . configure ( config ( ) ) ;
2016-04-06 22:51:58 -05:00
if ( Cache . empty ( ) )
{
Cache = getPathFromConfig ( " tile_cache_path " ) ;
}
if ( SysTemplate . empty ( ) )
{
SysTemplate = getPathFromConfig ( " sys_template_path " ) ;
}
if ( LoTemplate . empty ( ) )
{
LoTemplate = getPathFromConfig ( " lo_template_path " ) ;
}
if ( ChildRoot . empty ( ) )
{
ChildRoot = getPathFromConfig ( " child_root_path " ) ;
}
if ( LoSubPath . empty ( ) )
{
LoSubPath = getPathFromConfig ( " lo_jail_subpath " ) ;
}
2016-04-14 04:13:30 -05:00
if ( ServerName . empty ( ) )
{
ServerName = config ( ) . getString ( " server_name " ) ;
}
2016-04-06 22:51:58 -05:00
if ( FileServerRoot . empty ( ) )
{
FileServerRoot = getPathFromConfig ( " file_server_root_path " ) ;
}
if ( NumPreSpawnedChildren = = 0 )
{
2016-04-18 01:50:24 -05:00
// Default to 1 child.
NumPreSpawnedChildren = config ( ) . getUInt ( " num_prespawn_children " , 1 ) ;
2016-04-06 22:51:58 -05:00
}
2016-04-16 07:13:59 -05:00
StorageBase : : initialize ( ) ;
2015-03-17 18:56:15 -05:00
ServerApplication : : initialize ( self ) ;
}
2015-03-07 05:23:46 -06:00
2016-04-14 05:42:12 -05:00
# if ENABLE_SSL
2016-03-23 06:08:01 -05:00
void LOOLWSD : : initializeSSL ( )
{
2016-04-06 17:17:59 -05:00
const auto ssl_cert_file_path = getPathFromConfig ( " ssl.cert_file_path " ) ;
2016-03-23 06:08:01 -05:00
Log : : info ( " SSL Cert file: " + ssl_cert_file_path ) ;
2016-04-06 17:17:59 -05:00
const auto ssl_key_file_path = getPathFromConfig ( " ssl.key_file_path " ) ;
2016-03-23 06:08:01 -05:00
Log : : info ( " SSL Key file: " + ssl_key_file_path ) ;
2016-04-06 17:17:59 -05:00
const auto ssl_ca_file_path = getPathFromConfig ( " ssl.ca_file_path " ) ;
2016-03-24 21:42:37 -05:00
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
}
2016-04-08 04:24:52 -05:00
# endif
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 ) + " ), "
2016-05-04 06:06:34 -05:00
" must not be " + std : : to_string ( MasterPortNumber ) + " . " )
2015-10-28 04:55:03 -05:00
. required ( false )
. repeatable ( false )
. argument ( " port number " ) ) ;
2016-04-22 06:00:11 -05:00
optionSet . addOption ( Option ( " cache " , " " , " Path to a directory where to keep the tile cache (default: " + std : : string ( LOOLWSD_CACHEDIR ) + " ). " )
2015-10-28 04:55:03 -05:00
. 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 " ) ) ;
2016-04-06 22:36:54 -05:00
optionSet . addOption ( Option ( " admincreds " , " " , " Admin 'username/password' used to access the admin console. " )
. required ( false )
. repeatable ( false )
2016-05-04 13:33:13 -05:00
. argument ( " credentials " ) ) ;
2016-04-06 22:36:54 -05:00
2016-04-06 23:32:28 -05:00
optionSet . addOption ( Option ( " allowlocalstorage " , " " , " When true will allow highly insecure loading of files from local storage. " )
. required ( false )
. repeatable ( false ) ) ;
2016-04-12 04:00:33 -05:00
# if ENABLE_DEBUG
2016-04-05 11:41:10 -05:00
optionSet . addOption ( Option ( " unitlib " , " " , " Unit testing library path. " )
. required ( false )
. repeatable ( false )
. argument ( " unitlib " ) ) ;
2016-04-12 05:48:42 -05:00
2016-04-16 14:44:53 -05:00
optionSet . addOption ( Option ( " nocaps " , " " , " Use a non-privileged forkit for valgrinding. " )
. required ( false )
2016-04-18 01:49:21 -05:00
. repeatable ( false ) ) ;
2016-04-16 14:44:53 -05:00
2016-04-12 05:48:42 -05:00
optionSet . addOption ( Option ( " careerspan " , " " , " How many seconds to run. " )
. required ( false )
. repeatable ( false )
. argument ( " seconds " ) ) ;
# endif
2015-03-17 18:56:15 -05:00
}
2016-04-05 11:41:10 -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 " )
2016-04-15 09:07:24 -05:00
DisplayVersion = true ;
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 ) ;
2016-04-06 22:36:54 -05:00
else if ( optionName = = " admincreds " )
AdminCreds = value ;
2016-04-06 23:32:28 -05:00
else if ( optionName = = " allowlocalstorage " )
AllowLocalStorage = true ;
2016-04-12 04:00:33 -05:00
# if ENABLE_DEBUG
2016-04-05 11:41:10 -05:00
else if ( optionName = = " unitlib " )
UnitTestLibrary = value ;
2016-04-16 14:44:53 -05:00
else if ( optionName = = " nocaps " )
NoCapsForKit = true ;
2016-04-12 05:48:42 -05:00
else if ( optionName = = " careerspan " )
careerSpanSeconds = std : : stoi ( value ) ;
2016-05-04 06:06:34 -05:00
static const char * clientPort = getenv ( " LOOL_TEST_CLIENT_PORT " ) ;
if ( clientPort )
ClientPortNumber = std : : stoi ( clientPort ) ;
static const char * masterPort = getenv ( " LOOL_TEST_MASTER_PORT " ) ;
if ( masterPort )
MasterPortNumber = std : : stoi ( masterPort ) ;
2016-04-12 05:48:42 -05:00
# endif
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
2016-04-07 03:27:43 -05:00
Process : : PID LOOLWSD : : createForKit ( )
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 ( " --clientport= " + std : : to_string ( ClientPortNumber ) ) ;
2016-04-09 11:30:48 -05:00
if ( UnitWSD : : get ( ) . hasKitHooks ( ) )
args . push_back ( " --unitlib= " + UnitTestLibrary ) ;
2016-04-15 09:07:24 -05:00
if ( DisplayVersion )
args . push_back ( " --version " ) ;
2015-12-19 19:09:48 -06:00
2016-04-16 14:44:53 -05:00
std : : string forKitPath = Path ( Application : : instance ( ) . commandPath ( ) ) . parent ( ) . toString ( ) + " loolforkit " ;
if ( NoCapsForKit )
{
forKitPath = forKitPath + std : : string ( " -nocaps " ) ;
args . push_back ( " --nocaps " ) ;
}
2015-12-19 19:09:48 -06:00
2016-04-07 03:27:43 -05:00
Log : : info ( " Launching forkit process: " + forKitPath + " " +
2016-04-03 22:40:56 -05:00
Poco : : cat ( std : : string ( " " ) , args . begin ( ) , args . end ( ) ) ) ;
2015-12-19 19:09:48 -06:00
2016-04-07 03:27:43 -05:00
ProcessHandle child = Process : : launch ( forKitPath , 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-04-15 09:07:24 -05:00
if ( DisplayVersion )
Util : : displayVersionInfo ( " loolwsd " ) ;
2016-02-29 06:25:12 -06:00
2016-04-14 05:42:12 -05:00
# if ENABLE_SSL
2016-03-23 06:08:01 -05:00
initializeSSL ( ) ;
2016-04-08 04:24:52 -05:00
# endif
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-04-07 02:36:38 -05:00
Log : : syserror ( " Unable to access cache [ " + Cache +
" ] 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-04-18 07:33:22 -05:00
else if ( ChildRoot [ ChildRoot . size ( ) - 1 ] ! = ' / ' )
ChildRoot + = ' / ' ;
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 ( ) ;
2016-04-06 22:38:08 -05:00
FileServerRoot = Poco : : Path ( FileServerRoot ) . absolute ( ) . toString ( ) ;
Log : : debug ( " FileServerRoot: " + FileServerRoot ) ;
2016-03-20 09:07:24 -05:00
2016-05-04 06:06:34 -05:00
if ( ClientPortNumber = = MasterPortNumber )
2015-07-17 16:55:27 -05:00
throw IncompatibleOptionsException ( " port " ) ;
2016-04-06 22:36:54 -05:00
if ( AdminCreds . empty ( ) )
{
Log : : warn ( " No admin credentials set via 'admincreds' command-line argument. Admin Console will be disabled. " ) ;
}
2016-05-07 22:28:47 -05:00
// Create the directory where the fifo pipe with ForKit will be.
2016-04-18 07:33:22 -05:00
const Path pipePath = Path : : forDirectory ( ChildRoot + " / " + 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-04-07 03:04:05 -05:00
Log : : error ( " Failed to create pipe directory [ " + pipePath . toString ( ) + " ]. " ) ;
2016-02-13 11:22:15 -06:00
return Application : : EXIT_SOFTWARE ;
}
2016-05-07 22:28:47 -05:00
// Create the fifo with ForKit.
2016-04-03 22:40:56 -05:00
const std : : string pipeLoolwsd = Path ( pipePath , FIFO_LOOLWSD ) . toString ( ) ;
2016-04-12 09:49:20 -05:00
Log : : debug ( " mkfifo( " + pipeLoolwsd + " ) " ) ;
2016-04-03 22:40:56 -05:00
if ( mkfifo ( pipeLoolwsd . c_str ( ) , 0666 ) < 0 & & errno ! = EEXIST )
{
2016-04-12 09:57:58 -05:00
Log : : syserror ( " Failed to create fifo [ " + pipeLoolwsd + " ]. " ) ;
2016-04-03 22:40:56 -05:00
return Application : : EXIT_SOFTWARE ;
}
2016-03-20 05:59:32 -05:00
// Init the file server
FileServer fileServer ;
2016-01-31 21:34:18 -06:00
// Configure the Server.
2016-05-07 22:28:47 -05:00
// Note: TCPServer internally uses a ThreadPool to
// dispatch connections (the default if not given).
// The capacity of the ThreadPool is increased here to
// match MAX_SESSIONS. The pool must have sufficient available
// threads to dispatch new connections, otherwise 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-04-14 05:42:12 -05:00
# if ENABLE_SSL
2016-03-21 03:37:39 -05:00
SecureServerSocket svs ( ClientPortNumber ) ;
2016-04-08 04:24:52 -05:00
# else
ServerSocket svs ( ClientPortNumber ) ;
# endif
2016-01-31 21:34:18 -06:00
ThreadPool threadPool ( NumPreSpawnedChildren * 6 , MAX_SESSIONS * 2 ) ;
2016-04-06 07:43:44 -05:00
HTTPServer srv ( new ClientRequestHandlerFactory ( fileServer ) , threadPool , svs , params1 ) ;
2015-07-17 13:02:25 -05:00
srv . start ( ) ;
// And one on the port for child processes
2016-05-04 06:06:34 -05:00
SocketAddress addr2 ( " 127.0.0.1 " , MasterPortNumber ) ;
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-05-07 22:28:47 -05:00
// Fire the ForKit process; we are ready.
const Process : : PID forKitPid = createForKit ( ) ;
if ( forKitPid < 0 )
{
Log : : error ( " Failed to spawn loolforkit. " ) ;
return Application : : EXIT_SOFTWARE ;
}
// Open write fifo pipe with ForKit.
2016-04-07 03:27:43 -05:00
if ( ( ForKitWritePipe = open ( pipeLoolwsd . c_str ( ) , O_WRONLY ) ) < 0 )
2016-04-03 22:40:56 -05:00
{
2016-04-07 03:04:05 -05:00
Log : : syserror ( " Failed to open pipe [ " + pipeLoolwsd + " ] for writing. " ) ;
2016-04-03 22:40:56 -05:00
return Application : : EXIT_SOFTWARE ;
}
2016-04-12 09:49:20 -05:00
Log : : debug ( " open( " + pipeLoolwsd + " , WRONLY) = " + std : : to_string ( ForKitWritePipe ) ) ;
2016-04-03 22:40:56 -05:00
2016-05-07 22:28:47 -05:00
// Init the Admin manager
Admin : : instance ( ) . setForKitPid ( forKitPid ) ;
// Spawn some children, if necessary.
2016-04-04 09:17:03 -05:00
preForkChildren ( ) ;
2016-03-22 13:27:38 -05:00
time_t last30SecCheck = time ( NULL ) ;
2016-04-12 05:48:42 -05:00
# if ENABLE_DEBUG
2016-04-12 07:32:22 -05:00
time_t startTimeSpan = last30SecCheck ;
2016-04-12 05:48:42 -05:00
# endif
2015-12-28 17:24:29 -06:00
int status = 0 ;
2016-04-12 05:32:19 -05:00
while ( ! TerminationFlag )
2015-07-17 13:02:25 -05:00
{
2016-04-09 11:30:48 -05:00
UnitWSD : : get ( ) . invokeTest ( ) ;
2016-04-07 15:59:27 -05:00
2016-04-07 03:27:43 -05:00
const pid_t pid = waitpid ( forKitPid , & status , WUNTRACED | WNOHANG ) ;
2015-07-17 13:02:25 -05:00
if ( pid > 0 )
{
2016-04-07 03:27:43 -05:00
if ( forKitPid = = 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
< < 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
< < 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-04-07 03:04:05 -05:00
Log : : syserror ( " 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-04-06 02:15:16 -05:00
if ( ! std : : getenv ( " LOOL_NO_AUTOSAVE " ) )
2016-03-22 13:27:38 -05:00
{
2016-04-09 22:20:20 -05:00
if ( time ( nullptr ) > = last30SecCheck + 30 )
2016-03-22 13:27:38 -05:00
{
2016-04-09 22:20:20 -05:00
try
2016-03-22 13:27:38 -05:00
{
2016-04-09 22:20:20 -05:00
std : : unique_lock < std : : mutex > docBrokersLock ( docBrokersMutex ) ;
for ( auto & brokerIt : docBrokers )
2016-03-23 11:55:28 -05:00
{
2016-04-21 23:11:24 -05:00
if ( brokerIt . second - > isModified ( ) )
{
2016-04-25 19:48:02 -05:00
brokerIt . second - > autoSave ( false , 0 ) ;
2016-04-21 23:11:24 -05:00
}
2016-03-23 11:55:28 -05:00
}
2016-03-22 13:27:38 -05:00
}
2016-04-09 22:20:20 -05:00
catch ( const std : : exception & exc )
2016-03-22 13:27:38 -05:00
{
2016-04-09 22:20:20 -05:00
Log : : error ( " Exception: " + std : : string ( exc . what ( ) ) ) ;
2016-03-22 13:27:38 -05:00
}
2016-04-09 22:20:20 -05:00
last30SecCheck = time ( nullptr ) ;
2016-03-22 13:27:38 -05:00
}
}
2016-05-02 21:57:27 -05:00
2016-04-11 03:17:27 -05:00
sleep ( WSD_SLEEP_SECS ) ;
2016-05-02 21:57:27 -05:00
// Make sure we have sufficient reserves.
prespawnChildren ( ) ;
2015-07-24 13:10:24 -05:00
}
2016-04-12 05:48:42 -05:00
# if ENABLE_DEBUG
2016-04-12 07:32:22 -05:00
if ( careerSpanSeconds > 0 & & time ( nullptr ) > startTimeSpan + careerSpanSeconds )
2016-04-12 05:48:42 -05:00
{
2016-04-12 07:32:22 -05:00
Log : : info ( std : : to_string ( time ( nullptr ) - startTimeSpan ) + " seconds gone, finishing as requested. " ) ;
2016-04-13 03:43:08 -05:00
TerminationFlag = true ;
2016-04-12 05:48:42 -05:00
}
# endif
2015-07-17 13:02:25 -05:00
}
2015-04-16 11:15:40 -05:00
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-07 03:27:43 -05:00
Log : : info ( " Requesting child process " + std : : to_string ( forKitPid ) + " to terminate " ) ;
Util : : requestTermination ( forKitPid ) ;
2016-04-17 11:04:23 -05:00
for ( auto & child : newChildren )
{
child - > close ( true ) ;
}
2015-12-02 18:15:39 -06:00
2016-04-07 03:27:43 -05:00
// Wait for forkit process finish
waitpid ( forKitPid , & status , WUNTRACED ) ;
close ( ForKitWritePipe ) ;
2016-04-03 22:40:56 -05:00
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-04-14 05:42:12 -05:00
# if ENABLE_SSL
2016-03-21 03:37:39 -05:00
Poco : : Net : : uninitializeSSL ( ) ;
Poco : : Crypto : : uninitializeCrypto ( ) ;
2016-04-08 04:24:52 -05:00
# endif
2016-03-21 03:37:39 -05:00
2016-04-06 13:50:55 -05:00
Log : : info ( " Process [loolwsd] finished. " ) ;
int returnValue = Application : : EXIT_OK ;
2016-04-09 11:30:48 -05:00
UnitWSD : : get ( ) . returnValue ( returnValue ) ;
2016-04-06 13:50:55 -05:00
return returnValue ;
2015-03-17 18:56:15 -05:00
}
2015-03-04 17:14:04 -06:00
2016-04-09 11:30:48 -05:00
void UnitWSD : : testHandleRequest ( TestRequest type , UnitHTTPServerRequest & request , UnitHTTPServerResponse & response )
2016-04-07 15:59:27 -05:00
{
switch ( type )
{
case TestRequest : : TEST_REQ_CLIENT :
ClientRequestHandler : : handleClientRequest ( request , response ) ;
break ;
case TestRequest : : TEST_REQ_PRISONER :
PrisonerRequestHandler : : handlePrisonerRequest ( request , response ) ;
break ;
default :
assert ( false ) ;
break ;
}
}
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: */