2014-05-03 20:02:22 +02:00
/******************************************************************************
* Icinga 2 *
2018-01-02 12:06:00 +01:00
* Copyright ( C ) 2012 - 2018 Icinga Development Team ( https : //www.icinga.com/) *
2014-05-03 20:02:22 +02:00
* *
* This program is free software ; you can redistribute it and / or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation ; either version 2 *
* of the License , or ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License for more details . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program ; if not , write to the Free Software Foundation *
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 , USA . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-05-25 16:23:35 +02:00
# include "remote/apilistener.hpp"
2018-01-18 13:50:38 +01:00
# include "remote/apilistener-ti.cpp"
2015-06-22 11:11:21 +02:00
# include "remote/jsonrpcconnection.hpp"
2014-05-25 16:23:35 +02:00
# include "remote/endpoint.hpp"
2015-02-26 14:59:39 +01:00
# include "remote/jsonrpc.hpp"
2015-08-11 14:13:53 +02:00
# include "remote/apifunction.hpp"
2014-05-25 16:23:35 +02:00
# include "base/convert.hpp"
# include "base/netstring.hpp"
2014-10-26 19:59:49 +01:00
# include "base/json.hpp"
2015-08-15 20:28:05 +02:00
# include "base/configtype.hpp"
2014-10-19 14:21:12 +02:00
# include "base/logger.hpp"
2014-05-25 16:23:35 +02:00
# include "base/objectlock.hpp"
# include "base/stdiostream.hpp"
2017-05-11 17:30:20 +02:00
# include "base/perfdatavalue.hpp"
2014-05-25 16:23:35 +02:00
# include "base/application.hpp"
# include "base/context.hpp"
# include "base/statsfunction.hpp"
2014-12-15 10:16:06 +01:00
# include "base/exception.hpp"
2014-05-03 20:02:22 +02:00
# include <fstream>
using namespace icinga ;
REGISTER_TYPE ( ApiListener ) ;
boost : : signals2 : : signal < void ( bool ) > ApiListener : : OnMasterChanged ;
2015-11-24 15:25:55 +01:00
ApiListener : : Ptr ApiListener : : m_Instance ;
2014-05-03 20:02:22 +02:00
2015-09-21 11:44:58 +02:00
REGISTER_STATSFUNCTION ( ApiListener , & ApiListener : : StatsFunc ) ;
2014-05-03 20:02:22 +02:00
2015-08-11 14:13:53 +02:00
REGISTER_APIFUNCTION ( Hello , icinga , & ApiListener : : HelloAPIHandler ) ;
2018-01-04 04:25:35 +01:00
ApiListener : : ApiListener ( )
2016-06-14 08:19:13 +02:00
{
m_RelayQueue . SetName ( " ApiListener, RelayQueue " ) ;
m_SyncQueue . SetName ( " ApiListener, SyncQueue " ) ;
}
2015-03-02 09:56:09 +01:00
2018-01-04 04:25:35 +01:00
String ApiListener : : GetApiDir ( )
2017-09-05 14:21:30 +02:00
{
2018-08-09 15:37:23 +02:00
return Configuration : : DataDir + " /api/ " ;
2017-09-05 14:21:30 +02:00
}
2018-01-04 04:25:35 +01:00
String ApiListener : : GetCertsDir ( )
2017-09-05 14:21:30 +02:00
{
2018-08-09 15:37:23 +02:00
return Configuration : : DataDir + " /certs/ " ;
2017-09-05 14:21:30 +02:00
}
2018-01-04 04:25:35 +01:00
String ApiListener : : GetCaDir ( )
2017-09-05 14:21:30 +02:00
{
2018-08-09 15:37:23 +02:00
return Configuration : : DataDir + " /ca/ " ;
2017-09-05 14:21:30 +02:00
}
2018-01-04 04:25:35 +01:00
String ApiListener : : GetCertificateRequestsDir ( )
2017-09-05 14:21:30 +02:00
{
2018-08-09 15:37:23 +02:00
return Configuration : : DataDir + " /certificate-requests/ " ;
2017-09-05 14:21:30 +02:00
}
2018-01-04 04:25:35 +01:00
String ApiListener : : GetDefaultCertPath ( )
2017-10-16 15:32:57 +02:00
{
return GetCertsDir ( ) + " / " + ScriptGlobal : : Get ( " NodeName " ) + " .crt " ;
}
2018-01-04 04:25:35 +01:00
String ApiListener : : GetDefaultKeyPath ( )
2017-10-16 15:32:57 +02:00
{
return GetCertsDir ( ) + " / " + ScriptGlobal : : Get ( " NodeName " ) + " .key " ;
}
2018-01-04 04:25:35 +01:00
String ApiListener : : GetDefaultCaPath ( )
2017-10-16 15:32:57 +02:00
{
return GetCertsDir ( ) + " /ca.crt " ;
}
2018-09-13 18:05:31 +02:00
double ApiListener : : GetTlsHandshakeTimeout ( ) const
{
return Configuration : : TlsHandshakeTimeout ;
}
void ApiListener : : SetTlsHandshakeTimeout ( double value , bool suppress_events , const Value & cookie )
{
Configuration : : TlsHandshakeTimeout = value ;
}
2017-10-16 15:32:57 +02:00
void ApiListener : : CopyCertificateFile ( const String & oldCertPath , const String & newCertPath )
{
struct stat st1 , st2 ;
if ( ! oldCertPath . IsEmpty ( ) & & stat ( oldCertPath . CStr ( ) , & st1 ) > = 0 & & ( stat ( newCertPath . CStr ( ) , & st2 ) < 0 | | st1 . st_mtime > st2 . st_mtime ) ) {
Log ( LogWarning , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Copying ' " < < oldCertPath < < " ' certificate file to ' " < < newCertPath < < " ' " ;
2017-10-16 15:32:57 +02:00
Utility : : MkDirP ( Utility : : DirName ( newCertPath ) , 0700 ) ;
Utility : : CopyFile ( oldCertPath , newCertPath ) ;
}
}
2018-09-24 16:38:48 +02:00
/**
* Returns the API thread pool .
*
* @ returns The API thread pool .
*/
ThreadPool & ApiListener : : GetTP ( )
{
static ThreadPool tp ;
return tp ;
}
void ApiListener : : EnqueueAsyncCallback ( const std : : function < void ( ) > & callback , SchedulerPolicy policy )
{
GetTP ( ) . Post ( callback , policy ) ;
}
2018-01-04 04:25:35 +01:00
void ApiListener : : OnConfigLoaded ( )
2014-05-03 20:02:22 +02:00
{
2015-11-30 15:31:11 +01:00
if ( m_Instance )
BOOST_THROW_EXCEPTION ( ScriptError ( " Only one ApiListener object is allowed. " , GetDebugInfo ( ) ) ) ;
m_Instance = this ;
2017-10-16 15:32:57 +02:00
String defaultCertPath = GetDefaultCertPath ( ) ;
String defaultKeyPath = GetDefaultKeyPath ( ) ;
String defaultCaPath = GetDefaultCaPath ( ) ;
/* Migrate certificate location < 2.8 to the new default path. */
String oldCertPath = GetCertPath ( ) ;
String oldKeyPath = GetKeyPath ( ) ;
String oldCaPath = GetCaPath ( ) ;
CopyCertificateFile ( oldCertPath , defaultCertPath ) ;
CopyCertificateFile ( oldKeyPath , defaultKeyPath ) ;
CopyCertificateFile ( oldCaPath , defaultCaPath ) ;
if ( ! oldCertPath . IsEmpty ( ) & & ! oldKeyPath . IsEmpty ( ) & & ! oldCaPath . IsEmpty ( ) ) {
Log ( LogWarning , " ApiListener " , " Please read the upgrading documentation for v2.8: https://www.icinga.com/docs/icinga2/latest/doc/16-upgrading-icinga-2/ " ) ;
}
2014-05-03 20:02:22 +02:00
/* set up SSL context */
2017-11-21 13:20:55 +01:00
std : : shared_ptr < X509 > cert ;
2014-06-05 15:03:56 +02:00
try {
2017-10-16 15:32:57 +02:00
cert = GetX509Certificate ( defaultCertPath ) ;
2014-08-25 08:35:35 +02:00
} catch ( const std : : exception & ) {
2015-09-22 17:58:12 +02:00
BOOST_THROW_EXCEPTION ( ScriptError ( " Cannot get certificate from cert path: ' "
2017-12-19 15:50:05 +01:00
+ defaultCertPath + " '. " , GetDebugInfo ( ) ) ) ;
2014-06-05 15:03:56 +02:00
}
try {
SetIdentity ( GetCertificateCN ( cert ) ) ;
2014-08-25 08:35:35 +02:00
} catch ( const std : : exception & ) {
2015-09-22 17:58:12 +02:00
BOOST_THROW_EXCEPTION ( ScriptError ( " Cannot get certificate common name from cert path: ' "
2017-12-19 15:50:05 +01:00
+ defaultCertPath + " '. " , GetDebugInfo ( ) ) ) ;
2014-06-05 15:03:56 +02:00
}
2014-10-20 10:09:57 +02:00
Log ( LogInformation , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " My API identity: " < < GetIdentity ( ) ;
2014-05-03 20:02:22 +02:00
2017-08-30 13:33:38 +02:00
UpdateSSLContext ( ) ;
}
2018-01-04 04:25:35 +01:00
void ApiListener : : UpdateSSLContext ( )
2017-08-30 13:33:38 +02:00
{
2017-11-21 13:20:55 +01:00
std : : shared_ptr < SSL_CTX > context ;
2017-08-30 13:33:38 +02:00
2014-06-05 15:03:56 +02:00
try {
2017-10-16 15:32:57 +02:00
context = MakeSSLContext ( GetDefaultCertPath ( ) , GetDefaultKeyPath ( ) , GetDefaultCaPath ( ) ) ;
2014-08-25 08:35:35 +02:00
} catch ( const std : : exception & ) {
2015-09-22 17:58:12 +02:00
BOOST_THROW_EXCEPTION ( ScriptError ( " Cannot make SSL context for cert path: ' "
2017-12-19 15:50:05 +01:00
+ GetDefaultCertPath ( ) + " ' key path: ' " + GetDefaultKeyPath ( ) + " ' ca path: ' " + GetDefaultCaPath ( ) + " '. " , GetDebugInfo ( ) ) ) ;
2014-06-05 15:03:56 +02:00
}
2014-05-03 20:02:22 +02:00
2014-06-05 15:03:56 +02:00
if ( ! GetCrlPath ( ) . IsEmpty ( ) ) {
try {
2017-08-30 13:33:38 +02:00
AddCRLToSSLContext ( context , GetCrlPath ( ) ) ;
2014-08-25 08:35:35 +02:00
} catch ( const std : : exception & ) {
2015-09-22 17:58:12 +02:00
BOOST_THROW_EXCEPTION ( ScriptError ( " Cannot add certificate revocation list to SSL context for crl path: ' "
2017-12-19 15:50:05 +01:00
+ GetCrlPath ( ) + " '. " , GetDebugInfo ( ) ) ) ;
2014-06-05 15:03:56 +02:00
}
}
2016-03-25 21:25:19 +01:00
if ( ! GetCipherList ( ) . IsEmpty ( ) ) {
try {
2017-08-30 13:33:38 +02:00
SetCipherListToSSLContext ( context , GetCipherList ( ) ) ;
2016-03-25 21:25:19 +01:00
} catch ( const std : : exception & ) {
BOOST_THROW_EXCEPTION ( ScriptError ( " Cannot set cipher list to SSL context for cipher list: ' "
2017-12-19 15:50:05 +01:00
+ GetCipherList ( ) + " '. " , GetDebugInfo ( ) ) ) ;
2016-03-25 21:25:19 +01:00
}
}
2016-08-01 05:32:47 +02:00
if ( ! GetTlsProtocolmin ( ) . IsEmpty ( ) ) {
try {
2017-08-30 13:33:38 +02:00
SetTlsProtocolminToSSLContext ( context , GetTlsProtocolmin ( ) ) ;
2016-08-01 05:32:47 +02:00
} catch ( const std : : exception & ) {
BOOST_THROW_EXCEPTION ( ScriptError ( " Cannot set minimum TLS protocol version to SSL context with tls_protocolmin: ' " + GetTlsProtocolmin ( ) + " '. " , GetDebugInfo ( ) ) ) ;
}
}
2017-08-30 13:33:38 +02:00
m_SSLContext = context ;
2017-08-30 15:02:48 +02:00
for ( const Endpoint : : Ptr & endpoint : ConfigType : : GetObjectsByType < Endpoint > ( ) ) {
for ( const JsonRpcConnection : : Ptr & client : endpoint - > GetClients ( ) ) {
client - > Disconnect ( ) ;
}
}
for ( const JsonRpcConnection : : Ptr & client : m_AnonymousClients ) {
client - > Disconnect ( ) ;
}
2014-11-21 23:23:31 +01:00
}
2014-05-03 20:02:22 +02:00
2018-01-04 04:25:35 +01:00
void ApiListener : : OnAllConfigLoaded ( )
2014-11-21 23:23:31 +01:00
{
2015-11-24 15:25:55 +01:00
m_LocalEndpoint = Endpoint : : GetByName ( GetIdentity ( ) ) ;
if ( ! m_LocalEndpoint )
2015-02-26 17:09:45 +01:00
BOOST_THROW_EXCEPTION ( ScriptError ( " Endpoint object for ' " + GetIdentity ( ) + " ' is missing. " , GetDebugInfo ( ) ) ) ;
2014-05-03 20:02:22 +02:00
}
/**
* Starts the component .
*/
2015-08-20 17:18:48 +02:00
void ApiListener : : Start ( bool runtimeCreated )
2014-05-03 20:02:22 +02:00
{
2017-02-08 14:53:52 +01:00
Log ( LogInformation , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " ' " < < GetName ( ) < < " ' started. " ;
2017-02-08 14:53:52 +01:00
2014-11-21 23:23:31 +01:00
SyncZoneDirs ( ) ;
2015-08-20 17:18:48 +02:00
ObjectImpl < ApiListener > : : Start ( runtimeCreated ) ;
2014-05-03 20:02:22 +02:00
{
2017-12-31 19:12:33 +01:00
boost : : mutex : : scoped_lock lock ( m_LogLock ) ;
2014-05-03 20:02:22 +02:00
RotateLogFile ( ) ;
OpenLogFile ( ) ;
}
2018-07-26 17:09:06 +02:00
/* create the primary JSON-RPC listener */
if ( ! AddListener ( GetBindHost ( ) , GetBindPort ( ) ) ) {
Log ( LogCritical , " ApiListener " )
< < " Cannot add listener on host ' " < < GetBindHost ( ) < < " ' for port ' " < < GetBindPort ( ) < < " '. " ;
Application : : Exit ( EXIT_FAILURE ) ;
2014-08-04 16:34:17 +02:00
}
2014-05-03 20:02:22 +02:00
2014-11-08 21:17:16 +01:00
m_Timer = new Timer ( ) ;
2017-11-21 11:52:55 +01:00
m_Timer - > OnTimerExpired . connect ( std : : bind ( & ApiListener : : ApiTimerHandler , this ) ) ;
2014-05-03 20:02:22 +02:00
m_Timer - > SetInterval ( 5 ) ;
m_Timer - > Start ( ) ;
m_Timer - > Reschedule ( 0 ) ;
2016-07-21 12:27:18 +02:00
m_ReconnectTimer = new Timer ( ) ;
2017-11-21 11:52:55 +01:00
m_ReconnectTimer - > OnTimerExpired . connect ( std : : bind ( & ApiListener : : ApiReconnectTimerHandler , this ) ) ;
2018-09-24 17:28:27 +02:00
m_ReconnectTimer - > SetInterval ( 10 ) ;
2016-07-21 12:27:18 +02:00
m_ReconnectTimer - > Start ( ) ;
m_ReconnectTimer - > Reschedule ( 0 ) ;
2016-08-17 09:19:05 +02:00
m_AuthorityTimer = new Timer ( ) ;
2017-11-21 11:52:55 +01:00
m_AuthorityTimer - > OnTimerExpired . connect ( std : : bind ( & ApiListener : : UpdateObjectAuthority ) ) ;
2016-08-17 09:19:05 +02:00
m_AuthorityTimer - > SetInterval ( 30 ) ;
m_AuthorityTimer - > Start ( ) ;
2017-09-07 15:31:38 +02:00
m_CleanupCertificateRequestsTimer = new Timer ( ) ;
2017-11-21 11:52:55 +01:00
m_CleanupCertificateRequestsTimer - > OnTimerExpired . connect ( std : : bind ( & ApiListener : : CleanupCertificateRequestsTimerHandler , this ) ) ;
2017-09-07 15:31:38 +02:00
m_CleanupCertificateRequestsTimer - > SetInterval ( 3600 ) ;
m_CleanupCertificateRequestsTimer - > Start ( ) ;
m_CleanupCertificateRequestsTimer - > Reschedule ( 0 ) ;
2014-05-03 20:02:22 +02:00
OnMasterChanged ( true ) ;
}
2016-10-11 10:53:51 +02:00
void ApiListener : : Stop ( bool runtimeDeleted )
{
2016-10-24 08:40:12 +02:00
ObjectImpl < ApiListener > : : Stop ( runtimeDeleted ) ;
2017-02-08 14:53:52 +01:00
Log ( LogInformation , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " ' " < < GetName ( ) < < " ' stopped. " ;
2017-02-08 14:53:52 +01:00
2018-08-09 11:54:34 +02:00
{
boost : : mutex : : scoped_lock lock ( m_LogLock ) ;
CloseLogFile ( ) ;
}
RemoveStatusFile ( ) ;
2016-10-11 10:53:51 +02:00
}
2018-01-04 04:25:35 +01:00
ApiListener : : Ptr ApiListener : : GetInstance ( )
2014-05-03 20:02:22 +02:00
{
2015-11-24 15:25:55 +01:00
return m_Instance ;
2014-05-03 20:02:22 +02:00
}
2018-01-04 04:25:35 +01:00
Endpoint : : Ptr ApiListener : : GetMaster ( ) const
2014-05-03 20:02:22 +02:00
{
Zone : : Ptr zone = Zone : : GetLocalZone ( ) ;
2014-08-04 16:34:17 +02:00
if ( ! zone )
2017-11-30 08:36:35 +01:00
return nullptr ;
2014-08-04 16:34:17 +02:00
2014-05-03 20:02:22 +02:00
std : : vector < String > names ;
2016-08-25 06:19:44 +02:00
for ( const Endpoint : : Ptr & endpoint : zone - > GetEndpoints ( ) )
2015-10-22 10:52:38 +02:00
if ( endpoint - > GetConnected ( ) | | endpoint - > GetName ( ) = = GetIdentity ( ) )
2014-05-03 20:02:22 +02:00
names . push_back ( endpoint - > GetName ( ) ) ;
std : : sort ( names . begin ( ) , names . end ( ) ) ;
return Endpoint : : GetByName ( * names . begin ( ) ) ;
}
2018-01-04 04:25:35 +01:00
bool ApiListener : : IsMaster ( ) const
2014-05-03 20:02:22 +02:00
{
2014-08-04 16:34:17 +02:00
Endpoint : : Ptr master = GetMaster ( ) ;
if ( ! master )
return false ;
2015-11-24 15:25:55 +01:00
return master = = GetLocalEndpoint ( ) ;
2014-05-03 20:02:22 +02:00
}
/**
* Creates a new JSON - RPC listener on the specified port .
*
2014-08-25 08:27:19 +02:00
* @ param node The host the listener should be bound to .
2014-05-03 20:02:22 +02:00
* @ param service The port to listen on .
*/
2014-08-25 08:27:19 +02:00
bool ApiListener : : AddListener ( const String & node , const String & service )
2014-05-03 20:02:22 +02:00
{
ObjectLock olock ( this ) ;
2017-11-21 13:20:55 +01:00
std : : shared_ptr < SSL_CTX > sslContext = m_SSLContext ;
2014-05-03 20:02:22 +02:00
2014-06-05 14:36:57 +02:00
if ( ! sslContext ) {
Log ( LogCritical , " ApiListener " , " SSL context is required for AddListener() " ) ;
2014-08-04 16:34:17 +02:00
return false ;
2014-06-05 14:36:57 +02:00
}
2014-05-03 20:02:22 +02:00
2014-11-08 21:17:16 +01:00
TcpSocket : : Ptr server = new TcpSocket ( ) ;
2014-06-05 14:36:57 +02:00
try {
2014-08-25 08:27:19 +02:00
server - > Bind ( node , service , AF_UNSPEC ) ;
} catch ( const std : : exception & ) {
2014-10-20 10:09:57 +02:00
Log ( LogCritical , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Cannot bind TCP socket for host ' " < < node < < " ' on port ' " < < service < < " '. " ;
2014-08-04 16:34:17 +02:00
return false ;
2014-06-05 14:36:57 +02:00
}
2014-05-03 20:02:22 +02:00
2018-08-02 08:38:42 +02:00
Log ( LogInformation , " ApiListener " )
< < " Started new listener on ' " < < server - > GetClientAddress ( ) < < " ' " ;
2017-11-21 12:12:58 +01:00
std : : thread thread ( std : : bind ( & ApiListener : : ListenerThreadProc , this , server ) ) ;
2014-05-03 20:02:22 +02:00
thread . detach ( ) ;
m_Servers . insert ( server ) ;
2014-08-04 16:34:17 +02:00
2018-08-02 08:38:42 +02:00
UpdateStatusFile ( server ) ;
2014-08-04 16:34:17 +02:00
return true ;
2014-05-03 20:02:22 +02:00
}
void ApiListener : : ListenerThreadProc ( const Socket : : Ptr & server )
{
Utility : : SetThreadName ( " API Listener " ) ;
server - > Listen ( ) ;
for ( ; ; ) {
2014-06-05 16:17:53 +02:00
try {
Socket : : Ptr client = server - > Accept ( ) ;
2018-09-24 16:38:48 +02:00
/* Use dynamic thread pool with additional on demand resources with fast throughput. */
EnqueueAsyncCallback ( std : : bind ( & ApiListener : : NewClientHandler , this , client , String ( ) , RoleServer ) , LowLatencyScheduler ) ;
2014-08-25 08:35:35 +02:00
} catch ( const std : : exception & ) {
2014-06-05 16:17:53 +02:00
Log ( LogCritical , " ApiListener " , " Cannot accept new connection. " ) ;
}
2014-05-03 20:02:22 +02:00
}
}
/**
2014-06-23 10:00:02 +02:00
* Creates a new JSON - RPC client and connects to the specified endpoint .
2014-05-03 20:02:22 +02:00
*
2014-06-23 10:00:02 +02:00
* @ param endpoint The endpoint .
2014-05-03 20:02:22 +02:00
*/
2014-06-23 10:00:02 +02:00
void ApiListener : : AddConnection ( const Endpoint : : Ptr & endpoint )
2014-05-22 13:45:42 +02:00
{
2014-05-03 20:02:22 +02:00
{
ObjectLock olock ( this ) ;
2017-11-21 13:20:55 +01:00
std : : shared_ptr < SSL_CTX > sslContext = m_SSLContext ;
2014-05-03 20:02:22 +02:00
2014-06-05 14:36:57 +02:00
if ( ! sslContext ) {
2014-08-04 16:34:17 +02:00
Log ( LogCritical , " ApiListener " , " SSL context is required for AddConnection() " ) ;
2014-06-05 14:36:57 +02:00
return ;
}
2014-05-03 20:02:22 +02:00
}
2014-06-23 10:00:02 +02:00
String host = endpoint - > GetHost ( ) ;
String port = endpoint - > GetPort ( ) ;
2017-05-11 17:30:20 +02:00
Log ( LogInformation , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Reconnecting to endpoint ' " < < endpoint - > GetName ( ) < < " ' via host ' " < < host < < " ' and port ' " < < port < < " ' " ;
2014-08-22 15:39:34 +02:00
2014-11-08 21:17:16 +01:00
TcpSocket : : Ptr client = new TcpSocket ( ) ;
2014-05-03 20:02:22 +02:00
2014-05-22 13:45:42 +02:00
try {
2014-06-23 10:00:02 +02:00
client - > Connect ( host , port ) ;
2018-09-24 16:38:48 +02:00
2018-08-02 14:09:21 +02:00
NewClientHandler ( client , endpoint - > GetName ( ) , RoleClient ) ;
2018-09-24 16:38:48 +02:00
2014-06-23 10:00:02 +02:00
endpoint - > SetConnecting ( false ) ;
2014-05-22 13:45:42 +02:00
} catch ( const std : : exception & ex ) {
2014-06-23 10:00:02 +02:00
endpoint - > SetConnecting ( false ) ;
2014-08-04 13:35:12 +02:00
client - > Close ( ) ;
2014-06-23 10:00:02 +02:00
2014-10-20 10:09:57 +02:00
std : : ostringstream info ;
2014-06-23 10:00:02 +02:00
info < < " Cannot connect to host ' " < < host < < " ' on port ' " < < port < < " ' " ;
2014-06-05 14:36:57 +02:00
Log ( LogCritical , " ApiListener " , info . str ( ) ) ;
2014-10-20 10:09:57 +02:00
Log ( LogDebug , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < info . str ( ) < < " \n " < < DiagnosticInformation ( ex ) ;
2014-05-22 13:45:42 +02:00
}
2017-08-21 11:08:13 +02:00
Log ( LogInformation , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Finished reconnecting to endpoint ' " < < endpoint - > GetName ( ) < < " ' via host ' " < < host < < " ' and port ' " < < port < < " ' " ;
2014-05-03 20:02:22 +02:00
}
2015-08-11 12:08:54 +02:00
void ApiListener : : NewClientHandler ( const Socket : : Ptr & client , const String & hostname , ConnectionRole role )
{
try {
2015-08-11 12:56:30 +02:00
NewClientHandlerInternal ( client , hostname , role ) ;
2015-08-11 12:08:54 +02:00
} catch ( const std : : exception & ex ) {
Log ( LogCritical , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Exception while handling new API client connection: " < < DiagnosticInformation ( ex , false ) ;
2017-08-21 11:08:13 +02:00
Log ( LogDebug , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Exception while handling new API client connection: " < < DiagnosticInformation ( ex ) ;
2015-08-11 12:08:54 +02:00
}
}
2014-05-03 20:02:22 +02:00
/**
* Processes a new client connection .
*
* @ param client The new client .
*/
2015-08-11 12:08:54 +02:00
void ApiListener : : NewClientHandlerInternal ( const Socket : : Ptr & client , const String & hostname , ConnectionRole role )
2014-05-03 20:02:22 +02:00
{
CONTEXT ( " Handling new API client connection " ) ;
2016-08-02 12:14:03 +02:00
String conninfo ;
if ( role = = RoleClient )
conninfo = " to " ;
else
conninfo = " from " ;
conninfo + = " " + client - > GetPeerAddress ( ) ;
2014-05-03 20:02:22 +02:00
TlsStream : : Ptr tlsStream ;
2018-08-10 10:15:41 +02:00
String environmentName = Application : : GetAppEnvironment ( ) ;
2018-08-02 14:09:21 +02:00
String serverName = hostname ;
2018-08-10 10:15:41 +02:00
if ( ! environmentName . IsEmpty ( ) )
2018-08-02 14:09:21 +02:00
serverName + = " : " + environmentName ;
2014-05-03 20:02:22 +02:00
{
ObjectLock olock ( this ) ;
2014-06-05 15:03:56 +02:00
try {
2018-08-02 14:09:21 +02:00
tlsStream = new TlsStream ( client , serverName , role , m_SSLContext ) ;
2014-08-25 08:35:35 +02:00
} catch ( const std : : exception & ) {
2016-08-02 12:14:03 +02:00
Log ( LogCritical , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Cannot create TLS stream from client connection ( " < < conninfo < < " ) " ;
2014-06-05 15:03:56 +02:00
return ;
}
2014-05-03 20:02:22 +02:00
}
2014-06-05 15:03:56 +02:00
try {
tlsStream - > Handshake ( ) ;
2018-09-06 15:58:42 +02:00
} catch ( const std : : exception & ex ) {
2016-08-02 12:14:03 +02:00
Log ( LogCritical , " ApiListener " )
2018-09-06 15:58:42 +02:00
< < " Client TLS handshake failed ( " < < conninfo < < " ): " < < DiagnosticInformation ( ex , false ) ;
2018-06-14 15:03:04 +02:00
tlsStream - > Close ( ) ;
2014-06-05 15:03:56 +02:00
return ;
}
2014-05-03 20:02:22 +02:00
2017-11-21 13:20:55 +01:00
std : : shared_ptr < X509 > cert = tlsStream - > GetPeerCertificate ( ) ;
2014-06-05 15:03:56 +02:00
String identity ;
2015-06-22 11:11:21 +02:00
Endpoint : : Ptr endpoint ;
bool verify_ok = false ;
2014-06-05 15:03:56 +02:00
2015-06-22 11:11:21 +02:00
if ( cert ) {
try {
identity = GetCertificateCN ( cert ) ;
} catch ( const std : : exception & ) {
Log ( LogCritical , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Cannot get certificate common name from cert path: ' " < < GetDefaultCertPath ( ) < < " '. " ;
2018-06-14 15:03:04 +02:00
tlsStream - > Close ( ) ;
2015-06-22 11:11:21 +02:00
return ;
}
2014-05-03 20:02:22 +02:00
2015-06-22 11:11:21 +02:00
verify_ok = tlsStream - > IsVerifyOK ( ) ;
2016-02-09 15:55:12 +01:00
if ( ! hostname . IsEmpty ( ) ) {
if ( identity ! = hostname ) {
Log ( LogWarning , " ApiListener " )
< < " Unexpected certificate common name while connecting to endpoint ' "
2017-12-19 15:50:05 +01:00
< < hostname < < " ': got ' " < < identity < < " ' " ;
2018-06-14 15:03:04 +02:00
tlsStream - > Close ( ) ;
2016-02-09 15:55:12 +01:00
return ;
} else if ( ! verify_ok ) {
Log ( LogWarning , " ApiListener " )
2016-08-18 10:23:13 +02:00
< < " Certificate validation failed for endpoint ' " < < hostname
< < " ': " < < tlsStream - > GetVerifyError ( ) ;
2016-02-09 15:55:12 +01:00
}
}
2014-05-08 15:00:09 +02:00
2015-06-22 11:11:21 +02:00
if ( verify_ok )
endpoint = Endpoint : : GetByName ( identity ) ;
2016-07-21 13:48:00 +02:00
{
Log log ( LogInformation , " ApiListener " ) ;
2016-07-25 09:31:38 +02:00
log < < " New client connection for identity ' " < < identity < < " ' " < < conninfo ;
2016-07-21 13:48:00 +02:00
if ( ! verify_ok )
2016-07-21 22:00:32 +02:00
log < < " (certificate validation failed: " < < tlsStream - > GetVerifyError ( ) < < " ) " ;
2016-07-21 13:48:00 +02:00
else if ( ! endpoint )
log < < " (no Endpoint object found for identity) " ;
}
2015-06-22 11:11:21 +02:00
} else {
Log ( LogInformation , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " New client connection " < < conninfo < < " (no client certificate) " ;
2015-06-22 11:11:21 +02:00
}
2014-05-03 20:02:22 +02:00
2015-06-22 11:11:21 +02:00
ClientType ctype ;
2014-05-03 20:02:22 +02:00
2015-06-22 11:11:21 +02:00
if ( role = = RoleClient ) {
2018-01-11 11:17:38 +01:00
Dictionary : : Ptr message = new Dictionary ( {
{ " jsonrpc " , " 2.0 " } ,
{ " method " , " icinga::Hello " } ,
{ " params " , new Dictionary ( ) }
} ) ;
2015-06-22 11:11:21 +02:00
JsonRpc : : SendMessage ( tlsStream , message ) ;
ctype = ClientJsonRpc ;
} else {
2018-02-13 17:29:48 +01:00
tlsStream - > WaitForData ( 10 ) ;
2014-08-05 09:10:59 +02:00
2015-06-22 11:11:21 +02:00
if ( ! tlsStream - > IsDataAvailable ( ) ) {
2018-02-13 17:29:48 +01:00
if ( identity . IsEmpty ( ) )
Log ( LogInformation , " ApiListener " )
< < " No data received on new API connection. "
< < " Ensure that the remote endpoints are properly configured in a cluster setup. " ;
else
Log ( LogWarning , " ApiListener " )
< < " No data received on new API connection for identity ' " < < identity < < " '. "
< < " Ensure that the remote endpoints are properly configured in a cluster setup. " ;
2018-06-14 15:03:04 +02:00
tlsStream - > Close ( ) ;
2015-06-22 11:11:21 +02:00
return ;
}
char firstByte ;
tlsStream - > Peek ( & firstByte , 1 , false ) ;
2014-05-03 20:02:22 +02:00
2015-06-22 11:11:21 +02:00
if ( firstByte > = ' 0 ' & & firstByte < = ' 9 ' )
ctype = ClientJsonRpc ;
else
ctype = ClientHttp ;
}
if ( ctype = = ClientJsonRpc ) {
2015-09-30 08:41:09 +02:00
Log ( LogNotice , " ApiListener " , " New JSON-RPC client " ) ;
2015-06-22 11:11:21 +02:00
JsonRpcConnection : : Ptr aclient = new JsonRpcConnection ( identity , verify_ok , tlsStream , role ) ;
aclient - > Start ( ) ;
if ( endpoint ) {
2016-02-04 11:30:27 +01:00
bool needSync = ! endpoint - > GetConnected ( ) ;
2015-06-22 11:11:21 +02:00
endpoint - > AddClient ( aclient ) ;
2017-11-21 11:52:55 +01:00
m_SyncQueue . Enqueue ( std : : bind ( & ApiListener : : SyncClient , this , aclient , endpoint , needSync ) ) ;
2018-03-05 13:22:43 +01:00
} else {
if ( ! AddAnonymousClient ( aclient ) ) {
2018-09-05 17:45:35 +02:00
Log ( LogNotice , " ApiListener " )
< < " Ignoring anonymous JSON-RPC connection " < < conninfo
< < " . Max connections ( " < < GetMaxAnonymousClients ( ) < < " ) exceeded. " ;
2018-03-05 13:22:43 +01:00
aclient - > Disconnect ( ) ;
}
}
2015-06-22 11:11:21 +02:00
} else {
2015-09-30 08:41:09 +02:00
Log ( LogNotice , " ApiListener " , " New HTTP client " ) ;
2014-05-03 20:02:22 +02:00
2015-08-29 01:16:16 +02:00
HttpServerConnection : : Ptr aclient = new HttpServerConnection ( identity , verify_ok , tlsStream ) ;
2015-06-22 11:11:21 +02:00
aclient - > Start ( ) ;
AddHttpClient ( aclient ) ;
}
2014-05-03 20:02:22 +02:00
}
2016-02-04 11:30:27 +01:00
void ApiListener : : SyncClient ( const JsonRpcConnection : : Ptr & aclient , const Endpoint : : Ptr & endpoint , bool needSync )
2015-11-25 13:11:41 +01:00
{
2017-08-21 11:08:13 +02:00
Zone : : Ptr eZone = endpoint - > GetZone ( ) ;
2015-11-25 13:11:41 +01:00
try {
{
ObjectLock olock ( endpoint ) ;
endpoint - > SetSyncing ( true ) ;
}
2017-08-23 15:11:32 +02:00
Zone : : Ptr myZone = Zone : : GetLocalZone ( ) ;
if ( myZone - > GetParent ( ) = = eZone ) {
Log ( LogInformation , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Requesting new certificate for this Icinga instance from endpoint ' " < < endpoint - > GetName ( ) < < " '. " ;
2017-08-23 15:11:32 +02:00
2017-11-30 08:36:35 +01:00
JsonRpcConnection : : SendCertificateRequest ( aclient , nullptr , String ( ) ) ;
2017-09-04 13:18:06 +02:00
2017-09-06 12:11:48 +02:00
if ( Utility : : PathExists ( ApiListener : : GetCertificateRequestsDir ( ) ) )
2017-11-30 08:36:35 +01:00
Utility : : Glob ( ApiListener : : GetCertificateRequestsDir ( ) + " /*.json " , std : : bind ( & JsonRpcConnection : : SendCertificateRequest , aclient , nullptr , _1 ) , GlobFile ) ;
2017-08-23 15:11:32 +02:00
}
2016-02-04 11:30:27 +01:00
/* Make sure that the config updates are synced
* before the logs are replayed .
*/
2015-11-25 13:11:41 +01:00
Log ( LogInformation , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Sending config updates for endpoint ' " < < endpoint - > GetName ( ) < < " ' in zone ' " < < eZone - > GetName ( ) < < " '. " ;
2015-11-25 13:11:41 +01:00
/* sync zone file config */
SendConfigUpdate ( aclient ) ;
2017-08-21 11:08:13 +02:00
Log ( LogInformation , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Finished sending config file updates for endpoint ' " < < endpoint - > GetName ( ) < < " ' in zone ' " < < eZone - > GetName ( ) < < " '. " ;
2017-08-21 11:08:13 +02:00
2015-11-25 13:11:41 +01:00
/* sync runtime config */
SendRuntimeConfigObjects ( aclient ) ;
Log ( LogInformation , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Finished sending runtime config updates for endpoint ' " < < endpoint - > GetName ( ) < < " ' in zone ' " < < eZone - > GetName ( ) < < " '. " ;
2016-02-04 11:30:27 +01:00
2016-02-08 13:15:24 +01:00
if ( ! needSync ) {
ObjectLock olock2 ( endpoint ) ;
endpoint - > SetSyncing ( false ) ;
2016-02-04 11:30:27 +01:00
return ;
2016-02-08 13:15:24 +01:00
}
2016-02-04 11:30:27 +01:00
Log ( LogInformation , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Sending replay log for endpoint ' " < < endpoint - > GetName ( ) < < " ' in zone ' " < < eZone - > GetName ( ) < < " '. " ;
2015-11-25 13:11:41 +01:00
ReplayLog ( aclient ) ;
2016-02-04 11:30:27 +01:00
2017-08-21 11:08:13 +02:00
if ( eZone = = Zone : : GetLocalZone ( ) )
2016-05-11 13:04:39 +02:00
UpdateObjectAuthority ( ) ;
2016-02-04 11:30:27 +01:00
Log ( LogInformation , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Finished sending replay log for endpoint ' " < < endpoint - > GetName ( ) < < " ' in zone ' " < < eZone - > GetName ( ) < < " '. " ;
2015-11-25 13:11:41 +01:00
} catch ( const std : : exception & ex ) {
2017-08-21 11:08:13 +02:00
{
ObjectLock olock2 ( endpoint ) ;
endpoint - > SetSyncing ( false ) ;
}
2016-02-08 13:15:24 +01:00
2015-11-25 13:11:41 +01:00
Log ( LogCritical , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Error while syncing endpoint ' " < < endpoint - > GetName ( ) < < " ': " < < DiagnosticInformation ( ex , false ) ;
2017-08-21 11:08:13 +02:00
Log ( LogDebug , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Error while syncing endpoint ' " < < endpoint - > GetName ( ) < < " ': " < < DiagnosticInformation ( ex ) ;
2015-11-25 13:11:41 +01:00
}
2017-08-21 11:08:13 +02:00
Log ( LogInformation , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Finished syncing endpoint ' " < < endpoint - > GetName ( ) < < " ' in zone ' " < < eZone - > GetName ( ) < < " '. " ;
2015-11-25 13:11:41 +01:00
}
2018-01-04 04:25:35 +01:00
void ApiListener : : ApiTimerHandler ( )
2014-05-03 20:02:22 +02:00
{
double now = Utility : : GetTime ( ) ;
std : : vector < int > files ;
2017-11-23 06:51:48 +01:00
Utility : : Glob ( GetApiDir ( ) + " log/* " , std : : bind ( & ApiListener : : LogGlobHandler , std : : ref ( files ) , _1 ) , GlobFile ) ;
2014-05-03 20:02:22 +02:00
std : : sort ( files . begin ( ) , files . end ( ) ) ;
2016-08-25 06:19:44 +02:00
for ( int ts : files ) {
2014-05-03 20:02:22 +02:00
bool need = false ;
2016-08-25 06:19:44 +02:00
for ( const Endpoint : : Ptr & endpoint : ConfigType : : GetObjectsByType < Endpoint > ( ) ) {
2015-11-24 15:25:55 +01:00
if ( endpoint = = GetLocalEndpoint ( ) )
2014-05-03 20:02:22 +02:00
continue ;
if ( endpoint - > GetLogDuration ( ) > = 0 & & ts < now - endpoint - > GetLogDuration ( ) )
continue ;
if ( ts > endpoint - > GetLocalLogPosition ( ) ) {
need = true ;
break ;
}
}
if ( ! need ) {
String path = GetApiDir ( ) + " log/ " + Convert : : ToString ( ts ) ;
2014-10-20 10:09:57 +02:00
Log ( LogNotice , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Removing old log file: " < < path ;
2014-05-03 20:02:22 +02:00
( void ) unlink ( path . CStr ( ) ) ;
}
}
2016-08-25 06:19:44 +02:00
for ( const Endpoint : : Ptr & endpoint : ConfigType : : GetObjectsByType < Endpoint > ( ) ) {
2016-07-21 12:27:18 +02:00
if ( ! endpoint - > GetConnected ( ) )
continue ;
double ts = endpoint - > GetRemoteLogPosition ( ) ;
if ( ts = = 0 )
continue ;
2018-01-11 11:17:38 +01:00
Dictionary : : Ptr lmessage = new Dictionary ( {
{ " jsonrpc " , " 2.0 " } ,
{ " method " , " log::SetLogPosition " } ,
{ " params " , new Dictionary ( {
{ " log_position " , ts }
} ) }
} ) ;
2016-07-21 12:27:18 +02:00
double maxTs = 0 ;
2016-08-25 06:19:44 +02:00
for ( const JsonRpcConnection : : Ptr & client : endpoint - > GetClients ( ) ) {
2016-07-21 12:27:18 +02:00
if ( client - > GetTimestamp ( ) > maxTs )
maxTs = client - > GetTimestamp ( ) ;
}
2016-08-25 06:19:44 +02:00
for ( const JsonRpcConnection : : Ptr & client : endpoint - > GetClients ( ) ) {
2016-07-21 12:27:18 +02:00
if ( client - > GetTimestamp ( ) ! = maxTs )
client - > Disconnect ( ) ;
else
client - > SendMessage ( lmessage ) ;
}
Log ( LogNotice , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Setting log position for identity ' " < < endpoint - > GetName ( ) < < " ': "
< < Utility : : FormatDateTime ( " %Y/%m/%d %H:%M:%S " , ts ) ;
2016-07-21 12:27:18 +02:00
}
}
2018-01-04 04:25:35 +01:00
void ApiListener : : ApiReconnectTimerHandler ( )
2016-07-21 12:27:18 +02:00
{
2015-06-11 23:02:13 +02:00
Zone : : Ptr my_zone = Zone : : GetLocalZone ( ) ;
2016-08-25 06:19:44 +02:00
for ( const Zone : : Ptr & zone : ConfigType : : GetObjectsByType < Zone > ( ) ) {
2015-10-15 17:15:19 +02:00
/* don't connect to global zones */
if ( zone - > GetGlobal ( ) )
continue ;
2015-06-11 23:02:13 +02:00
/* only connect to endpoints in a) the same zone b) our parent zone c) immediate child zones */
if ( my_zone ! = zone & & my_zone ! = zone - > GetParent ( ) & & zone ! = my_zone - > GetParent ( ) ) {
Log ( LogDebug , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Not connecting to Zone ' " < < zone - > GetName ( )
< < " ' because it's not in the same zone, a parent or a child zone. " ;
2015-06-11 23:02:13 +02:00
continue ;
}
2014-05-03 20:02:22 +02:00
2016-08-25 06:19:44 +02:00
for ( const Endpoint : : Ptr & endpoint : zone - > GetEndpoints ( ) ) {
2015-06-11 23:02:13 +02:00
/* don't connect to ourselves */
2015-11-24 15:25:55 +01:00
if ( endpoint = = GetLocalEndpoint ( ) ) {
2015-02-17 15:46:03 +01:00
Log ( LogDebug , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Not connecting to Endpoint ' " < < endpoint - > GetName ( ) < < " ' because that's us. " ;
2014-05-03 20:02:22 +02:00
continue ;
2015-02-17 15:46:03 +01:00
}
2014-05-03 20:02:22 +02:00
2015-06-11 23:02:13 +02:00
/* don't try to connect to endpoints which don't have a host and port */
if ( endpoint - > GetHost ( ) . IsEmpty ( ) | | endpoint - > GetPort ( ) . IsEmpty ( ) ) {
Log ( LogDebug , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Not connecting to Endpoint ' " < < endpoint - > GetName ( )
< < " ' because the host/port attributes are missing. " ;
2015-06-11 23:02:13 +02:00
continue ;
2014-05-08 15:00:09 +02:00
}
2014-05-03 20:02:22 +02:00
2015-06-11 23:02:13 +02:00
/* don't try to connect if there's already a connection attempt */
if ( endpoint - > GetConnecting ( ) ) {
2015-02-17 15:46:03 +01:00
Log ( LogDebug , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Not connecting to Endpoint ' " < < endpoint - > GetName ( )
< < " ' because we're already trying to connect to it. " ;
2014-05-03 20:02:22 +02:00
continue ;
2015-02-17 15:46:03 +01:00
}
2014-05-03 20:02:22 +02:00
2015-06-11 23:02:13 +02:00
/* don't try to connect if we're already connected */
2015-10-22 10:52:38 +02:00
if ( endpoint - > GetConnected ( ) ) {
2015-06-11 23:02:13 +02:00
Log ( LogDebug , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Not connecting to Endpoint ' " < < endpoint - > GetName ( )
< < " ' because we're already connected to it. " ;
2015-06-11 23:02:13 +02:00
continue ;
2014-05-08 15:00:09 +02:00
}
2015-06-11 23:02:13 +02:00
2018-09-24 16:38:48 +02:00
/* Set connecting state to prevent duplicated queue inserts later. */
endpoint - > SetConnecting ( true ) ;
/* Use dynamic thread pool with additional on demand resources with fast throughput. */
EnqueueAsyncCallback ( std : : bind ( & ApiListener : : AddConnection , this , endpoint ) , LowLatencyScheduler ) ;
2014-05-03 20:02:22 +02:00
}
}
2014-08-04 16:34:17 +02:00
Endpoint : : Ptr master = GetMaster ( ) ;
if ( master )
2014-10-20 10:09:57 +02:00
Log ( LogNotice , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Current zone master: " < < master - > GetName ( ) ;
2014-05-03 20:02:22 +02:00
std : : vector < String > names ;
2016-08-25 06:19:44 +02:00
for ( const Endpoint : : Ptr & endpoint : ConfigType : : GetObjectsByType < Endpoint > ( ) )
2015-10-22 10:52:38 +02:00
if ( endpoint - > GetConnected ( ) )
2017-11-30 08:19:58 +01:00
names . emplace_back ( endpoint - > GetName ( ) + " ( " + Convert : : ToString ( endpoint - > GetClients ( ) . size ( ) ) + " ) " ) ;
2014-05-03 20:02:22 +02:00
2014-10-20 10:09:57 +02:00
Log ( LogNotice , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Connected endpoints: " < < Utility : : NaturalJoin ( names ) ;
2014-05-03 20:02:22 +02:00
}
2017-09-07 15:31:38 +02:00
static void CleanupCertificateRequest ( const String & path , double expiryTime )
{
# ifndef _WIN32
struct stat statbuf ;
if ( lstat ( path . CStr ( ) , & statbuf ) < 0 )
return ;
# else /* _WIN32 */
struct _stat statbuf ;
if ( _stat ( path . CStr ( ) , & statbuf ) < 0 )
return ;
# endif /* _WIN32 */
if ( statbuf . st_mtime < expiryTime )
( void ) unlink ( path . CStr ( ) ) ;
}
2018-01-04 04:25:35 +01:00
void ApiListener : : CleanupCertificateRequestsTimerHandler ( )
2017-09-07 15:31:38 +02:00
{
String requestsDir = GetCertificateRequestsDir ( ) ;
if ( Utility : : PathExists ( requestsDir ) ) {
/* remove certificate requests that are older than a week */
double expiryTime = Utility : : GetTime ( ) - 7 * 24 * 60 * 60 ;
2017-11-21 11:52:55 +01:00
Utility : : Glob ( requestsDir + " /*.json " , std : : bind ( & CleanupCertificateRequest , _1 , expiryTime ) , GlobFile ) ;
2017-09-07 15:31:38 +02:00
}
}
2015-09-22 17:58:12 +02:00
void ApiListener : : RelayMessage ( const MessageOrigin : : Ptr & origin ,
2017-12-19 15:50:05 +01:00
const ConfigObject : : Ptr & secobj , const Dictionary : : Ptr & message , bool log )
2014-05-03 20:02:22 +02:00
{
2016-02-09 12:46:11 +01:00
if ( ! IsActive ( ) )
return ;
2017-11-21 11:52:55 +01:00
m_RelayQueue . Enqueue ( std : : bind ( & ApiListener : : SyncRelayMessage , this , origin , secobj , message , log ) , PriorityNormal , true ) ;
2014-05-03 20:02:22 +02:00
}
2015-08-15 20:28:05 +02:00
void ApiListener : : PersistMessage ( const Dictionary : : Ptr & message , const ConfigObject : : Ptr & secobj )
2014-05-03 20:02:22 +02:00
{
double ts = message - > Get ( " ts " ) ;
ASSERT ( ts ! = 0 ) ;
2014-11-08 21:17:16 +01:00
Dictionary : : Ptr pmessage = new Dictionary ( ) ;
2014-05-03 20:02:22 +02:00
pmessage - > Set ( " timestamp " , ts ) ;
2014-10-26 19:59:49 +01:00
pmessage - > Set ( " message " , JsonEncode ( message ) ) ;
2016-06-15 11:26:35 +02:00
if ( secobj ) {
Dictionary : : Ptr secname = new Dictionary ( ) ;
2016-08-15 14:39:33 +02:00
secname - > Set ( " type " , secobj - > GetReflectionType ( ) - > GetName ( ) ) ;
2016-06-15 11:26:35 +02:00
secname - > Set ( " name " , secobj - > GetName ( ) ) ;
pmessage - > Set ( " secobj " , secname ) ;
}
2014-05-03 20:02:22 +02:00
boost : : mutex : : scoped_lock lock ( m_LogLock ) ;
if ( m_LogFile ) {
2014-10-26 19:59:49 +01:00
NetString : : WriteStringToStream ( m_LogFile , JsonEncode ( pmessage ) ) ;
2014-05-03 20:02:22 +02:00
m_LogMessageCount + + ;
SetLogMessageTimestamp ( ts ) ;
if ( m_LogMessageCount > 50000 ) {
CloseLogFile ( ) ;
RotateLogFile ( ) ;
OpenLogFile ( ) ;
}
}
}
2014-11-13 11:23:57 +01:00
void ApiListener : : SyncSendMessage ( const Endpoint : : Ptr & endpoint , const Dictionary : : Ptr & message )
{
ObjectLock olock ( endpoint ) ;
if ( ! endpoint - > GetSyncing ( ) ) {
Log ( LogNotice , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Sending message ' " < < message - > Get ( " method " ) < < " ' to ' " < < endpoint - > GetName ( ) < < " ' " ;
2014-11-13 11:23:57 +01:00
2016-01-25 10:57:06 +01:00
double maxTs = 0 ;
2016-08-25 06:19:44 +02:00
for ( const JsonRpcConnection : : Ptr & client : endpoint - > GetClients ( ) ) {
2016-01-25 10:57:06 +01:00
if ( client - > GetTimestamp ( ) > maxTs )
maxTs = client - > GetTimestamp ( ) ;
}
2016-08-25 06:19:44 +02:00
for ( const JsonRpcConnection : : Ptr & client : endpoint - > GetClients ( ) ) {
2016-01-25 10:57:06 +01:00
if ( client - > GetTimestamp ( ) ! = maxTs )
continue ;
2014-11-13 11:23:57 +01:00
client - > SendMessage ( message ) ;
2016-01-25 10:57:06 +01:00
}
2014-11-13 11:23:57 +01:00
}
}
2016-01-27 13:39:31 +01:00
bool ApiListener : : RelayMessageOne ( const Zone : : Ptr & targetZone , const MessageOrigin : : Ptr & origin , const Dictionary : : Ptr & message , const Endpoint : : Ptr & currentMaster )
2014-05-03 20:02:22 +02:00
{
2016-01-27 12:18:16 +01:00
ASSERT ( targetZone ) ;
2014-05-03 20:02:22 +02:00
2016-01-27 12:18:16 +01:00
Zone : : Ptr myZone = Zone : : GetLocalZone ( ) ;
2016-11-11 10:41:49 +01:00
/* only relay the message to a) the same zone, b) the parent zone and c) direct child zones. Exception is a global zone. */
if ( ! targetZone - > GetGlobal ( ) & &
2017-12-19 15:50:05 +01:00
targetZone ! = myZone & &
targetZone ! = myZone - > GetParent ( ) & &
targetZone - > GetParent ( ) ! = myZone ) {
2016-01-27 12:18:16 +01:00
return true ;
2016-11-11 10:41:49 +01:00
}
2014-05-03 20:02:22 +02:00
2016-01-27 13:39:31 +01:00
Endpoint : : Ptr myEndpoint = GetLocalEndpoint ( ) ;
2014-05-03 20:02:22 +02:00
std : : vector < Endpoint : : Ptr > skippedEndpoints ;
2016-01-27 12:18:16 +01:00
bool relayed = false , log_needed = false , log_done = false ;
2016-11-11 10:41:49 +01:00
std : : set < Endpoint : : Ptr > targetEndpoints ;
if ( targetZone - > GetGlobal ( ) ) {
targetEndpoints = myZone - > GetEndpoints ( ) ;
for ( const Zone : : Ptr & zone : ConfigType : : GetObjectsByType < Zone > ( ) ) {
/* Fetch immediate child zone members */
if ( zone - > GetParent ( ) = = myZone ) {
std : : set < Endpoint : : Ptr > endpoints = zone - > GetEndpoints ( ) ;
targetEndpoints . insert ( endpoints . begin ( ) , endpoints . end ( ) ) ;
}
}
} else {
targetEndpoints = targetZone - > GetEndpoints ( ) ;
}
for ( const Endpoint : : Ptr & endpoint : targetEndpoints ) {
2015-10-01 09:17:23 +02:00
/* don't relay messages to ourselves */
2015-11-24 15:25:55 +01:00
if ( endpoint = = GetLocalEndpoint ( ) )
2014-05-03 20:02:22 +02:00
continue ;
2016-01-27 12:18:16 +01:00
log_needed = true ;
2015-10-01 09:17:23 +02:00
/* don't relay messages to disconnected endpoints */
2015-10-22 10:52:38 +02:00
if ( ! endpoint - > GetConnected ( ) ) {
2016-01-27 12:18:16 +01:00
if ( targetZone = = myZone )
log_done = false ;
2015-10-15 09:28:20 +02:00
2015-10-01 09:17:23 +02:00
continue ;
2015-10-15 09:28:20 +02:00
}
2015-10-01 09:17:23 +02:00
2016-01-27 12:18:16 +01:00
log_done = true ;
2015-10-01 09:17:23 +02:00
2015-10-15 09:28:20 +02:00
/* don't relay the message to the zone through more than one endpoint unless this is our own zone */
2016-01-27 12:18:16 +01:00
if ( relayed & & targetZone ! = myZone ) {
2014-05-03 20:02:22 +02:00
skippedEndpoints . push_back ( endpoint ) ;
continue ;
}
/* don't relay messages back to the endpoint which we got the message from */
2015-08-04 14:47:44 +02:00
if ( origin & & origin - > FromClient & & endpoint = = origin - > FromClient - > GetEndpoint ( ) ) {
2014-05-03 20:02:22 +02:00
skippedEndpoints . push_back ( endpoint ) ;
continue ;
}
/* don't relay messages back to the zone which we got the message from */
2016-01-27 12:18:16 +01:00
if ( origin & & origin - > FromZone & & targetZone = = origin - > FromZone ) {
2014-05-03 20:02:22 +02:00
skippedEndpoints . push_back ( endpoint ) ;
continue ;
}
/* only relay message to the master if we're not currently the master */
2016-01-27 13:39:31 +01:00
if ( currentMaster ! = myEndpoint & & currentMaster ! = endpoint ) {
2014-05-03 20:02:22 +02:00
skippedEndpoints . push_back ( endpoint ) ;
continue ;
}
2016-01-27 12:18:16 +01:00
relayed = true ;
2014-05-03 20:02:22 +02:00
2014-11-13 11:23:57 +01:00
SyncSendMessage ( endpoint , message ) ;
2014-05-03 20:02:22 +02:00
}
2016-01-27 12:18:16 +01:00
if ( ! skippedEndpoints . empty ( ) ) {
double ts = message - > Get ( " ts " ) ;
2015-10-01 09:17:23 +02:00
2016-08-25 06:19:44 +02:00
for ( const Endpoint : : Ptr & endpoint : skippedEndpoints )
2016-01-27 12:18:16 +01:00
endpoint - > SetLocalLogPosition ( ts ) ;
}
return ! log_needed | | log_done ;
}
void ApiListener : : SyncRelayMessage ( const MessageOrigin : : Ptr & origin ,
2017-12-19 15:50:05 +01:00
const ConfigObject : : Ptr & secobj , const Dictionary : : Ptr & message , bool log )
2016-01-27 12:18:16 +01:00
{
double ts = Utility : : GetTime ( ) ;
message - > Set ( " ts " , ts ) ;
Log ( LogNotice , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Relaying ' " < < message - > Get ( " method " ) < < " ' message " ;
2016-01-27 12:18:16 +01:00
if ( origin & & origin - > FromZone )
message - > Set ( " originZone " , origin - > FromZone - > GetName ( ) ) ;
Zone : : Ptr target_zone ;
if ( secobj ) {
if ( secobj - > GetReflectionType ( ) = = Zone : : TypeInstance )
target_zone = static_pointer_cast < Zone > ( secobj ) ;
else
target_zone = static_pointer_cast < Zone > ( secobj - > GetZone ( ) ) ;
}
if ( ! target_zone )
target_zone = Zone : : GetLocalZone ( ) ;
2016-01-27 13:39:31 +01:00
Endpoint : : Ptr master = GetMaster ( ) ;
bool need_log = ! RelayMessageOne ( target_zone , origin , message , master ) ;
2016-01-27 12:18:16 +01:00
2018-08-08 13:59:43 +02:00
for ( const Zone : : Ptr & zone : target_zone - > GetAllParentsRaw ( ) ) {
2016-01-27 13:39:31 +01:00
if ( ! RelayMessageOne ( zone , origin , message , master ) )
2016-01-27 12:18:16 +01:00
need_log = true ;
}
if ( log & & need_log )
PersistMessage ( message , secobj ) ;
2014-05-03 20:02:22 +02:00
}
/* must hold m_LogLock */
2018-01-04 04:25:35 +01:00
void ApiListener : : OpenLogFile ( )
2014-05-03 20:02:22 +02:00
{
String path = GetApiDir ( ) + " log/current " ;
2017-02-08 13:06:31 +01:00
Utility : : MkDirP ( Utility : : DirName ( path ) , 0750 ) ;
2018-01-04 09:07:03 +01:00
auto * fp = new std : : fstream ( path . CStr ( ) , std : : fstream : : out | std : : ofstream : : app ) ;
2014-05-03 20:02:22 +02:00
if ( ! fp - > good ( ) ) {
2014-10-20 10:09:57 +02:00
Log ( LogWarning , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Could not open spool file: " < < path ;
2014-05-03 20:02:22 +02:00
return ;
}
2014-11-08 21:17:16 +01:00
m_LogFile = new StdioStream ( fp , true ) ;
2014-05-03 20:02:22 +02:00
m_LogMessageCount = 0 ;
SetLogMessageTimestamp ( Utility : : GetTime ( ) ) ;
}
/* must hold m_LogLock */
2018-01-04 04:25:35 +01:00
void ApiListener : : CloseLogFile ( )
2014-05-03 20:02:22 +02:00
{
if ( ! m_LogFile )
return ;
m_LogFile - > Close ( ) ;
m_LogFile . reset ( ) ;
}
/* must hold m_LogLock */
2018-01-04 04:25:35 +01:00
void ApiListener : : RotateLogFile ( )
2014-05-03 20:02:22 +02:00
{
double ts = GetLogMessageTimestamp ( ) ;
if ( ts = = 0 )
ts = Utility : : GetTime ( ) ;
String oldpath = GetApiDir ( ) + " log/current " ;
String newpath = GetApiDir ( ) + " log/ " + Convert : : ToString ( static_cast < int > ( ts ) + 1 ) ;
2018-06-22 11:12:09 +02:00
# ifdef _WIN32
_unlink ( newpath . CStr ( ) ) ;
# endif /* _WIN32 */
2014-05-03 20:02:22 +02:00
( void ) rename ( oldpath . CStr ( ) , newpath . CStr ( ) ) ;
}
void ApiListener : : LogGlobHandler ( std : : vector < int > & files , const String & file )
{
String name = Utility : : BaseName ( file ) ;
2015-09-29 11:02:08 +02:00
if ( name = = " current " )
return ;
2014-05-03 20:02:22 +02:00
int ts ;
try {
ts = Convert : : ToLong ( name ) ;
2015-09-29 11:02:08 +02:00
} catch ( const std : : exception & ) {
2014-05-03 20:02:22 +02:00
return ;
}
files . push_back ( ts ) ;
}
2015-06-22 11:11:21 +02:00
void ApiListener : : ReplayLog ( const JsonRpcConnection : : Ptr & client )
2014-05-03 20:02:22 +02:00
{
Endpoint : : Ptr endpoint = client - > GetEndpoint ( ) ;
2016-01-20 14:07:07 +01:00
if ( endpoint - > GetLogDuration ( ) = = 0 ) {
ObjectLock olock2 ( endpoint ) ;
endpoint - > SetSyncing ( false ) ;
2016-01-19 17:25:28 +01:00
return ;
2016-01-20 14:07:07 +01:00
}
2016-01-19 17:25:28 +01:00
2014-05-03 20:02:22 +02:00
CONTEXT ( " Replaying log for Endpoint ' " + endpoint - > GetName ( ) + " ' " ) ;
int count = - 1 ;
double peer_ts = endpoint - > GetLocalLogPosition ( ) ;
2015-02-26 14:59:39 +01:00
double logpos_ts = peer_ts ;
2014-05-03 20:02:22 +02:00
bool last_sync = false ;
2015-09-22 17:58:12 +02:00
2014-08-26 10:24:04 +02:00
Endpoint : : Ptr target_endpoint = client - > GetEndpoint ( ) ;
ASSERT ( target_endpoint ) ;
2015-09-22 17:58:12 +02:00
2014-08-26 10:24:04 +02:00
Zone : : Ptr target_zone = target_endpoint - > GetZone ( ) ;
2015-09-22 17:58:12 +02:00
2016-02-08 13:15:24 +01:00
if ( ! target_zone ) {
ObjectLock olock2 ( endpoint ) ;
endpoint - > SetSyncing ( false ) ;
2014-08-26 10:24:04 +02:00
return ;
2016-02-08 13:15:24 +01:00
}
2014-05-03 20:02:22 +02:00
for ( ; ; ) {
boost : : mutex : : scoped_lock lock ( m_LogLock ) ;
CloseLogFile ( ) ;
RotateLogFile ( ) ;
if ( count = = - 1 | | count > 50000 ) {
OpenLogFile ( ) ;
lock . unlock ( ) ;
} else {
last_sync = true ;
}
count = 0 ;
std : : vector < int > files ;
2017-11-23 06:51:48 +01:00
Utility : : Glob ( GetApiDir ( ) + " log/* " , std : : bind ( & ApiListener : : LogGlobHandler , std : : ref ( files ) , _1 ) , GlobFile ) ;
2014-05-03 20:02:22 +02:00
std : : sort ( files . begin ( ) , files . end ( ) ) ;
2016-08-25 06:19:44 +02:00
for ( int ts : files ) {
2014-05-03 20:02:22 +02:00
String path = GetApiDir ( ) + " log/ " + Convert : : ToString ( ts ) ;
if ( ts < peer_ts )
continue ;
2014-10-20 10:09:57 +02:00
Log ( LogNotice , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Replaying log: " < < path ;
2014-05-03 20:02:22 +02:00
2018-01-04 09:07:03 +01:00
auto * fp = new std : : fstream ( path . CStr ( ) , std : : fstream : : in | std : : fstream : : binary ) ;
2014-11-08 21:17:16 +01:00
StdioStream : : Ptr logStream = new StdioStream ( fp , true ) ;
2014-05-03 20:02:22 +02:00
String message ;
2015-02-14 16:34:36 +01:00
StreamReadContext src ;
2014-05-03 20:02:22 +02:00
while ( true ) {
Dictionary : : Ptr pmessage ;
try {
2015-02-14 18:48:33 +01:00
StreamReadStatus srs = NetString : : ReadStringFromStream ( logStream , & message , src ) ;
if ( srs = = StatusEof )
2014-05-03 20:02:22 +02:00
break ;
2015-02-14 18:48:33 +01:00
if ( srs ! = StatusNewItem )
continue ;
2014-10-26 19:59:49 +01:00
pmessage = JsonDecode ( message ) ;
2014-05-03 20:02:22 +02:00
} catch ( const std : : exception & ) {
2014-10-20 10:09:57 +02:00
Log ( LogWarning , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Unexpected end-of-file for cluster log: " < < path ;
2014-05-03 20:02:22 +02:00
/* Log files may be incomplete or corrupted. This is perfectly OK. */
break ;
}
if ( pmessage - > Get ( " timestamp " ) < = peer_ts )
continue ;
2014-10-24 17:48:02 +02:00
Dictionary : : Ptr secname = pmessage - > Get ( " secobj " ) ;
2015-09-22 17:58:12 +02:00
2014-08-26 10:24:04 +02:00
if ( secname ) {
2016-08-16 11:02:10 +02:00
ConfigObject : : Ptr secobj = ConfigObject : : GetObject ( secname - > Get ( " type " ) , secname - > Get ( " name " ) ) ;
2015-09-22 17:58:12 +02:00
2014-08-26 10:24:04 +02:00
if ( ! secobj )
continue ;
2015-09-22 17:58:12 +02:00
2014-08-26 10:24:04 +02:00
if ( ! target_zone - > CanAccessObject ( secobj ) )
continue ;
}
2015-10-26 07:56:58 +01:00
try {
2017-11-13 16:30:29 +01:00
size_t bytesSent = NetString : : WriteStringToStream ( client - > GetStream ( ) , pmessage - > Get ( " message " ) ) ;
endpoint - > AddMessageSent ( bytesSent ) ;
2015-10-26 07:56:58 +01:00
count + + ;
} catch ( const std : : exception & ex ) {
Log ( LogWarning , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Error while replaying log for endpoint ' " < < endpoint - > GetName ( ) < < " ': " < < DiagnosticInformation ( ex , false ) ;
2017-08-21 11:08:13 +02:00
Log ( LogDebug , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Error while replaying log for endpoint ' " < < endpoint - > GetName ( ) < < " ': " < < DiagnosticInformation ( ex ) ;
2017-08-21 11:08:13 +02:00
2015-10-26 07:56:58 +01:00
break ;
}
2014-05-03 20:02:22 +02:00
peer_ts = pmessage - > Get ( " timestamp " ) ;
2015-02-26 14:59:39 +01:00
if ( ts > logpos_ts + 10 ) {
logpos_ts = ts ;
2018-01-11 11:17:38 +01:00
Dictionary : : Ptr lmessage = new Dictionary ( {
{ " jsonrpc " , " 2.0 " } ,
{ " method " , " log::SetLogPosition " } ,
{ " params " , new Dictionary ( {
{ " log_position " , logpos_ts }
} ) }
} ) ;
2015-02-26 14:59:39 +01:00
2017-11-13 16:30:29 +01:00
size_t bytesSent = JsonRpc : : SendMessage ( client - > GetStream ( ) , lmessage ) ;
endpoint - > AddMessageSent ( bytesSent ) ;
2015-02-26 14:59:39 +01:00
}
2014-05-03 20:02:22 +02:00
}
2014-05-09 10:23:54 +02:00
logStream - > Close ( ) ;
2014-05-03 20:02:22 +02:00
}
2015-09-11 14:09:46 +02:00
if ( count > 0 ) {
Log ( LogInformation , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Replayed " < < count < < " messages. " ;
2015-09-11 14:09:46 +02:00
}
2014-10-20 10:09:57 +02:00
Log ( LogNotice , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Replayed " < < count < < " messages. " ;
2014-05-03 20:02:22 +02:00
if ( last_sync ) {
{
ObjectLock olock2 ( endpoint ) ;
endpoint - > SetSyncing ( false ) ;
}
OpenLogFile ( ) ;
break ;
}
}
}
2015-02-13 11:28:43 +01:00
void ApiListener : : StatsFunc ( const Dictionary : : Ptr & status , const Array : : Ptr & perfdata )
2014-05-03 20:02:22 +02:00
{
std : : pair < Dictionary : : Ptr , Dictionary : : Ptr > stats ;
ApiListener : : Ptr listener = ApiListener : : GetInstance ( ) ;
if ( ! listener )
2015-02-07 22:36:17 +01:00
return ;
2014-05-03 20:02:22 +02:00
stats = listener - > GetStatus ( ) ;
2014-11-13 11:23:57 +01:00
ObjectLock olock ( stats . second ) ;
2016-08-25 06:19:44 +02:00
for ( const Dictionary : : Pair & kv : stats . second )
2017-05-11 17:30:20 +02:00
perfdata - > Add ( new PerfdataValue ( " api_ " + kv . first , kv . second ) ) ;
2014-05-03 20:02:22 +02:00
status - > Set ( " api " , stats . first ) ;
}
2018-01-04 04:25:35 +01:00
std : : pair < Dictionary : : Ptr , Dictionary : : Ptr > ApiListener : : GetStatus ( )
2014-05-03 20:02:22 +02:00
{
2014-11-08 21:17:16 +01:00
Dictionary : : Ptr perfdata = new Dictionary ( ) ;
2014-05-03 20:02:22 +02:00
/* cluster stats */
2015-09-23 18:09:46 +02:00
double allEndpoints = 0 ;
Array : : Ptr allNotConnectedEndpoints = new Array ( ) ;
Array : : Ptr allConnectedEndpoints = new Array ( ) ;
2014-05-03 20:02:22 +02:00
2015-07-31 16:19:58 +02:00
Zone : : Ptr my_zone = Zone : : GetLocalZone ( ) ;
2015-09-23 18:09:46 +02:00
Dictionary : : Ptr connectedZones = new Dictionary ( ) ;
2016-08-25 06:19:44 +02:00
for ( const Zone : : Ptr & zone : ConfigType : : GetObjectsByType < Zone > ( ) ) {
2015-07-31 16:19:58 +02:00
/* only check endpoints in a) the same zone b) our parent zone c) immediate child zones */
if ( my_zone ! = zone & & my_zone ! = zone - > GetParent ( ) & & zone ! = my_zone - > GetParent ( ) ) {
Log ( LogDebug , " ApiListener " )
2017-12-19 15:50:05 +01:00
< < " Not checking connection to Zone ' " < < zone - > GetName ( ) < < " ' because it's not in the same zone, a parent or a child zone. " ;
2014-05-03 20:02:22 +02:00
continue ;
2015-07-31 16:19:58 +02:00
}
2014-05-03 20:02:22 +02:00
2015-09-23 18:09:46 +02:00
bool zoneConnected = false ;
int countZoneEndpoints = 0 ;
double zoneLag = 0 ;
2018-01-11 11:17:38 +01:00
ArrayData zoneEndpoints ;
2015-09-23 18:09:46 +02:00
2016-08-25 06:19:44 +02:00
for ( const Endpoint : : Ptr & endpoint : zone - > GetEndpoints ( ) ) {
2018-01-11 11:17:38 +01:00
zoneEndpoints . emplace_back ( endpoint - > GetName ( ) ) ;
2015-09-23 18:09:46 +02:00
2015-07-31 16:19:58 +02:00
if ( endpoint - > GetName ( ) = = GetIdentity ( ) )
continue ;
2014-05-03 20:02:22 +02:00
2015-09-25 14:23:42 +02:00
double eplag = CalculateZoneLag ( endpoint ) ;
2015-07-31 16:19:58 +02:00
2015-09-25 14:23:42 +02:00
if ( eplag > 0 & & eplag > zoneLag )
2015-09-23 18:09:46 +02:00
zoneLag = eplag ;
allEndpoints + + ;
countZoneEndpoints + + ;
2015-10-22 10:52:38 +02:00
if ( ! endpoint - > GetConnected ( ) ) {
2015-09-23 18:09:46 +02:00
allNotConnectedEndpoints - > Add ( endpoint - > GetName ( ) ) ;
} else {
allConnectedEndpoints - > Add ( endpoint - > GetName ( ) ) ;
zoneConnected = true ;
}
2015-07-31 16:19:58 +02:00
}
2015-09-23 18:09:46 +02:00
/* if there's only one endpoint inside the zone, we're not connected - that's us, fake it */
if ( zone - > GetEndpoints ( ) . size ( ) = = 1 & & countZoneEndpoints = = 0 )
zoneConnected = true ;
String parentZoneName ;
Zone : : Ptr parentZone = zone - > GetParent ( ) ;
if ( parentZone )
parentZoneName = parentZone - > GetName ( ) ;
2018-01-11 11:17:38 +01:00
Dictionary : : Ptr zoneStats = new Dictionary ( {
{ " connected " , zoneConnected } ,
{ " client_log_lag " , zoneLag } ,
{ " endpoints " , new Array ( std : : move ( zoneEndpoints ) ) } ,
{ " parent_zone " , parentZoneName }
} ) ;
2015-09-23 18:09:46 +02:00
connectedZones - > Set ( zone - > GetName ( ) , zoneStats ) ;
2014-05-03 20:02:22 +02:00
}
2017-05-11 17:30:20 +02:00
/* connection stats */
2018-09-05 17:45:35 +02:00
size_t jsonRpcAnonymousClients = GetAnonymousClients ( ) . size ( ) ;
2017-05-11 17:30:20 +02:00
size_t httpClients = GetHttpClients ( ) . size ( ) ;
size_t workQueueItems = JsonRpcConnection : : GetWorkQueueLength ( ) ;
size_t workQueueCount = JsonRpcConnection : : GetWorkQueueCount ( ) ;
size_t syncQueueItems = m_SyncQueue . GetLength ( ) ;
size_t relayQueueItems = m_RelayQueue . GetLength ( ) ;
double workQueueItemRate = JsonRpcConnection : : GetWorkQueueRate ( ) ;
double syncQueueItemRate = m_SyncQueue . GetTaskCount ( 60 ) / 60.0 ;
double relayQueueItemRate = m_RelayQueue . GetTaskCount ( 60 ) / 60.0 ;
2018-01-11 11:17:38 +01:00
Dictionary : : Ptr status = new Dictionary ( {
{ " identity " , GetIdentity ( ) } ,
{ " num_endpoints " , allEndpoints } ,
{ " num_conn_endpoints " , allConnectedEndpoints - > GetLength ( ) } ,
{ " num_not_conn_endpoints " , allNotConnectedEndpoints - > GetLength ( ) } ,
{ " conn_endpoints " , allConnectedEndpoints } ,
{ " not_conn_endpoints " , allNotConnectedEndpoints } ,
{ " zones " , connectedZones } ,
{ " json_rpc " , new Dictionary ( {
2018-09-05 17:45:35 +02:00
{ " anonymous_clients " , jsonRpcAnonymousClients } ,
2018-01-11 11:17:38 +01:00
{ " work_queue_items " , workQueueItems } ,
{ " work_queue_count " , workQueueCount } ,
{ " sync_queue_items " , syncQueueItems } ,
{ " relay_queue_items " , relayQueueItems } ,
{ " work_queue_item_rate " , workQueueItemRate } ,
{ " sync_queue_item_rate " , syncQueueItemRate } ,
{ " relay_queue_item_rate " , relayQueueItemRate }
} ) } ,
{ " http " , new Dictionary ( {
{ " clients " , httpClients }
} ) }
} ) ;
2017-05-11 17:30:20 +02:00
/* performance data */
2015-09-23 18:09:46 +02:00
perfdata - > Set ( " num_endpoints " , allEndpoints ) ;
perfdata - > Set ( " num_conn_endpoints " , Convert : : ToDouble ( allConnectedEndpoints - > GetLength ( ) ) ) ;
perfdata - > Set ( " num_not_conn_endpoints " , Convert : : ToDouble ( allNotConnectedEndpoints - > GetLength ( ) ) ) ;
2014-05-03 20:02:22 +02:00
2018-09-05 17:45:35 +02:00
perfdata - > Set ( " num_json_rpc_anonymous_clients " , jsonRpcAnonymousClients ) ;
2017-05-11 17:30:20 +02:00
perfdata - > Set ( " num_http_clients " , httpClients ) ;
perfdata - > Set ( " num_json_rpc_work_queue_items " , workQueueItems ) ;
perfdata - > Set ( " num_json_rpc_work_queue_count " , workQueueCount ) ;
perfdata - > Set ( " num_json_rpc_sync_queue_items " , syncQueueItems ) ;
perfdata - > Set ( " num_json_rpc_relay_queue_items " , relayQueueItems ) ;
perfdata - > Set ( " num_json_rpc_work_queue_item_rate " , workQueueItemRate ) ;
perfdata - > Set ( " num_json_rpc_sync_queue_item_rate " , syncQueueItemRate ) ;
perfdata - > Set ( " num_json_rpc_relay_queue_item_rate " , relayQueueItemRate ) ;
2014-05-03 20:02:22 +02:00
return std : : make_pair ( status , perfdata ) ;
}
2014-05-08 15:12:56 +02:00
2015-09-25 14:23:42 +02:00
double ApiListener : : CalculateZoneLag ( const Endpoint : : Ptr & endpoint )
{
double remoteLogPosition = endpoint - > GetRemoteLogPosition ( ) ;
double eplag = Utility : : GetTime ( ) - remoteLogPosition ;
2015-10-22 10:52:38 +02:00
if ( ( endpoint - > GetSyncing ( ) | | ! endpoint - > GetConnected ( ) ) & & remoteLogPosition ! = 0 )
2015-09-25 14:23:42 +02:00
return eplag ;
return 0 ;
}
2018-03-05 13:22:43 +01:00
bool ApiListener : : AddAnonymousClient ( const JsonRpcConnection : : Ptr & aclient )
2014-05-08 15:12:56 +02:00
{
2017-11-27 16:06:59 +01:00
boost : : mutex : : scoped_lock lock ( m_AnonymousClientsLock ) ;
2018-09-05 17:45:35 +02:00
if ( GetMaxAnonymousClients ( ) > = 0 & & m_AnonymousClients . size ( ) + 1 > GetMaxAnonymousClients ( ) )
2018-03-05 13:22:43 +01:00
return false ;
2014-05-08 15:12:56 +02:00
m_AnonymousClients . insert ( aclient ) ;
2018-03-05 13:22:43 +01:00
return true ;
2014-05-08 15:12:56 +02:00
}
2015-06-22 11:11:21 +02:00
void ApiListener : : RemoveAnonymousClient ( const JsonRpcConnection : : Ptr & aclient )
2014-05-08 15:12:56 +02:00
{
2017-11-27 16:06:59 +01:00
boost : : mutex : : scoped_lock lock ( m_AnonymousClientsLock ) ;
2014-05-08 15:12:56 +02:00
m_AnonymousClients . erase ( aclient ) ;
}
2018-01-04 04:25:35 +01:00
std : : set < JsonRpcConnection : : Ptr > ApiListener : : GetAnonymousClients ( ) const
2014-05-08 15:12:56 +02:00
{
2017-11-27 16:06:59 +01:00
boost : : mutex : : scoped_lock lock ( m_AnonymousClientsLock ) ;
2014-05-08 15:12:56 +02:00
return m_AnonymousClients ;
2014-05-09 10:23:54 +02:00
}
2015-06-22 11:11:21 +02:00
2015-08-29 01:16:16 +02:00
void ApiListener : : AddHttpClient ( const HttpServerConnection : : Ptr & aclient )
2015-06-22 11:11:21 +02:00
{
2017-11-27 16:06:59 +01:00
boost : : mutex : : scoped_lock lock ( m_HttpClientsLock ) ;
2015-06-22 11:11:21 +02:00
m_HttpClients . insert ( aclient ) ;
}
2015-08-29 01:16:16 +02:00
void ApiListener : : RemoveHttpClient ( const HttpServerConnection : : Ptr & aclient )
2015-06-22 11:11:21 +02:00
{
2017-11-27 16:06:59 +01:00
boost : : mutex : : scoped_lock lock ( m_HttpClientsLock ) ;
2015-06-22 11:11:21 +02:00
m_HttpClients . erase ( aclient ) ;
}
2018-01-04 04:25:35 +01:00
std : : set < HttpServerConnection : : Ptr > ApiListener : : GetHttpClients ( ) const
2015-06-22 11:11:21 +02:00
{
2017-11-27 16:06:59 +01:00
boost : : mutex : : scoped_lock lock ( m_HttpClientsLock ) ;
2015-06-22 11:11:21 +02:00
return m_HttpClients ;
}
2015-08-11 14:13:53 +02:00
2015-08-04 14:47:44 +02:00
Value ApiListener : : HelloAPIHandler ( const MessageOrigin : : Ptr & origin , const Dictionary : : Ptr & params )
2015-08-11 14:13:53 +02:00
{
return Empty ;
}
2015-11-24 15:25:55 +01:00
2018-01-04 04:25:35 +01:00
Endpoint : : Ptr ApiListener : : GetLocalEndpoint ( ) const
2015-11-24 15:25:55 +01:00
{
return m_LocalEndpoint ;
}
2016-08-01 05:32:47 +02:00
2018-01-11 07:08:09 +01:00
void ApiListener : : ValidateTlsProtocolmin ( const Lazy < String > & lvalue , const ValidationUtils & utils )
2016-08-01 05:32:47 +02:00
{
2018-01-11 07:08:09 +01:00
ObjectImpl < ApiListener > : : ValidateTlsProtocolmin ( lvalue , utils ) ;
2016-08-01 05:32:47 +02:00
2018-01-11 07:08:09 +01:00
if ( lvalue ( ) ! = SSL_TXT_TLSV1
2016-08-08 15:27:16 +02:00
# ifdef SSL_TXT_TLSV1_1
2018-01-11 07:08:09 +01:00
& & lvalue ( ) ! = SSL_TXT_TLSV1_1 & &
lvalue ( ) ! = SSL_TXT_TLSV1_2
2016-08-08 15:27:16 +02:00
# endif /* SSL_TXT_TLSV1_1 */
2017-12-19 15:50:05 +01:00
) {
2016-08-09 15:41:27 +02:00
String message = " Invalid TLS version. Must be one of ' " SSL_TXT_TLSV1 " ' " ;
2016-08-08 15:27:16 +02:00
# ifdef SSL_TXT_TLSV1_1
2016-08-09 15:41:27 +02:00
message + = " , ' " SSL_TXT_TLSV1_1 " ' or ' " SSL_TXT_TLSV1_2 " ' " ;
2016-08-08 15:27:16 +02:00
# endif /* SSL_TXT_TLSV1_1 */
2016-08-09 15:41:27 +02:00
2017-11-30 18:09:38 +01:00
BOOST_THROW_EXCEPTION ( ValidationError ( this , { " tls_protocolmin " } , message ) ) ;
2016-08-01 05:32:47 +02:00
}
}
2016-08-04 10:12:55 +02:00
2018-09-13 18:05:31 +02:00
void ApiListener : : ValidateTlsHandshakeTimeout ( const Lazy < double > & lvalue , const ValidationUtils & utils )
{
ObjectImpl < ApiListener > : : ValidateTlsHandshakeTimeout ( lvalue , utils ) ;
if ( lvalue ( ) < = 0 )
BOOST_THROW_EXCEPTION ( ValidationError ( this , { " tls_handshake_timeout " } , " Value must be greater than 0. " ) ) ;
}
2018-01-04 04:25:35 +01:00
bool ApiListener : : IsHACluster ( )
2016-08-04 10:12:55 +02:00
{
Zone : : Ptr zone = Zone : : GetLocalZone ( ) ;
if ( ! zone )
return false ;
return zone - > IsSingleInstance ( ) ;
}
2017-05-12 10:48:11 +02:00
/* Provide a helper function for zone origin name. */
String ApiListener : : GetFromZoneName ( const Zone : : Ptr & fromZone )
{
String fromZoneName ;
if ( fromZone ) {
fromZoneName = fromZone - > GetName ( ) ;
} else {
Zone : : Ptr lzone = Zone : : GetLocalZone ( ) ;
if ( lzone )
fromZoneName = lzone - > GetName ( ) ;
}
return fromZoneName ;
}
2018-08-02 08:38:42 +02:00
void ApiListener : : UpdateStatusFile ( TcpSocket : : Ptr socket )
{
2018-08-09 15:37:23 +02:00
String path = Configuration : : CacheDir + " /api-state.json " ;
2018-08-02 08:38:42 +02:00
std : : pair < String , String > details = socket - > GetClientAddressDetails ( ) ;
Utility : : SaveJsonFile ( path , 0644 , new Dictionary ( {
{ " host " , details . first } ,
2018-08-22 12:57:47 +02:00
{ " port " , Convert : : ToLong ( details . second ) }
2018-08-02 08:38:42 +02:00
} ) ) ;
}
2018-08-09 11:54:34 +02:00
void ApiListener : : RemoveStatusFile ( )
{
2018-08-09 15:37:23 +02:00
String path = Configuration : : CacheDir + " /api-state.json " ;
2018-08-09 11:54:34 +02:00
if ( Utility : : PathExists ( path ) ) {
if ( unlink ( path . CStr ( ) ) < 0 & & errno ! = ENOENT ) {
BOOST_THROW_EXCEPTION ( posix_error ( )
< < boost : : errinfo_api_function ( " unlink " )
< < boost : : errinfo_errno ( errno )
< < boost : : errinfo_file_name ( path ) ) ;
}
}
}