2019-02-25 14:48:22 +01:00
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2014-05-03 20:02:22 +02:00
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"
2019-04-16 16:37:38 +02:00
# include "remote/configpackageutility.hpp"
2019-05-10 12:48:34 +02:00
# include "remote/configobjectutility.hpp"
2014-05-25 16:23:35 +02:00
# include "base/convert.hpp"
2019-02-19 18:06:14 +01:00
# include "base/defer.hpp"
2019-02-08 11:43:47 +01:00
# include "base/io-engine.hpp"
2014-05-25 16:23:35 +02:00
# 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"
2019-02-25 16:18:48 +01:00
# include "base/tcpsocket.hpp"
2019-02-12 14:56:47 +01:00
# include <boost/asio/buffer.hpp>
2020-02-05 15:53:56 +01:00
# include <boost/asio/io_context_strand.hpp>
2019-02-08 11:43:47 +01:00
# include <boost/asio/ip/tcp.hpp>
# include <boost/asio/spawn.hpp>
2019-02-08 14:23:10 +01:00
# include <boost/asio/ssl/context.hpp>
2020-02-05 15:53:56 +01:00
# include <boost/date_time/posix_time/posix_time_duration.hpp>
2020-04-03 11:10:08 +02:00
# include <boost/lexical_cast.hpp>
# include <boost/regex.hpp>
2019-02-12 14:56:47 +01:00
# include <boost/system/error_code.hpp>
2019-02-08 11:43:47 +01:00
# include <climits>
2020-02-05 15:53:56 +01:00
# include <cstdint>
2014-05-03 20:02:22 +02:00
# include <fstream>
2019-02-08 11:43:47 +01:00
# include <memory>
2019-02-12 14:56:25 +01:00
# include <openssl/ssl.h>
2019-02-08 18:00:53 +01:00
# include <openssl/tls1.h>
2019-02-12 14:56:25 +01:00
# include <openssl/x509.h>
2019-02-08 18:00:53 +01:00
# include <sstream>
2019-04-17 15:42:39 +02:00
# include <utility>
2014-05-03 20:02:22 +02:00
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-09-27 17:12:02 +02:00
String ApiListener : : GetApiZonesDir ( )
{
return GetApiDir ( ) + " zones/ " ;
}
String ApiListener : : GetApiZonesStageDir ( )
{
return GetApiDir ( ) + " zones-stage/ " ;
}
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-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 ( ) ) {
2018-10-18 09:50:53 +02:00
Log ( LogWarning , " ApiListener " , " Please read the upgrading documentation for v2.8: https://icinga.com/docs/icinga2/latest/doc/16-upgrading-icinga-2/ " ) ;
2017-10-16 15:32:57 +02:00
}
2019-05-10 12:48:34 +02:00
/* Create the internal API object storage. */
ConfigObjectUtility : : CreateStorage ( ) ;
2019-04-16 16:37:38 +02:00
/* Cache API packages and their active stage name. */
UpdateActivePackageStagesCache ( ) ;
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
{
2021-07-16 18:31:52 +02:00
m_SSLContext = SetupSslContext ( GetDefaultCertPath ( ) , GetDefaultKeyPath ( ) , GetDefaultCaPath ( ) , GetCrlPath ( ) , GetCipherList ( ) , GetTlsProtocolmin ( ) , GetDebugInfo ( ) ) ;
2019-02-20 12:00:11 +01: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
2018-10-25 14:10:30 +02:00
SyncLocalZoneDirs ( ) ;
2014-11-21 23:23:31 +01:00
2015-08-20 17:18:48 +02:00
ObjectImpl < ApiListener > : : Start ( runtimeCreated ) ;
2014-05-03 20:02:22 +02:00
{
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > lock ( m_LogLock ) ;
2014-05-03 20:02:22 +02:00
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 ( ) ;
2021-01-18 14:29:05 +01:00
m_Timer - > OnTimerExpired . connect ( [ this ] ( const Timer * const & ) { ApiTimerHandler ( ) ; } ) ;
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 ( ) ;
2021-01-18 14:29:05 +01:00
m_ReconnectTimer - > OnTimerExpired . connect ( [ this ] ( const Timer * const & ) { ApiReconnectTimerHandler ( ) ; } ) ;
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 ) ;
2019-03-29 15:53:56 +01:00
/* Keep this in relative sync with the cold startup in UpdateObjectAuthority() and the reconnect interval above.
* Previous : 60 s reconnect , 30 s OA , 60 s cold startup .
2020-03-31 17:54:54 +02:00
* Now : 10 s reconnect , 10 s OA , 30 s cold startup .
2019-03-29 15:53:56 +01:00
*/
2016-08-17 09:19:05 +02:00
m_AuthorityTimer = new Timer ( ) ;
2021-01-18 14:29:05 +01:00
m_AuthorityTimer - > OnTimerExpired . connect ( [ ] ( const Timer * const & ) { UpdateObjectAuthority ( ) ; } ) ;
2019-03-29 15:53:56 +01:00
m_AuthorityTimer - > SetInterval ( 10 ) ;
2016-08-17 09:19:05 +02:00
m_AuthorityTimer - > Start ( ) ;
2017-09-07 15:31:38 +02:00
m_CleanupCertificateRequestsTimer = new Timer ( ) ;
2021-01-18 14:29:05 +01:00
m_CleanupCertificateRequestsTimer - > OnTimerExpired . connect ( [ this ] ( const Timer * const & ) { CleanupCertificateRequestsTimerHandler ( ) ; } ) ;
2017-09-07 15:31:38 +02:00
m_CleanupCertificateRequestsTimer - > SetInterval ( 3600 ) ;
m_CleanupCertificateRequestsTimer - > Start ( ) ;
m_CleanupCertificateRequestsTimer - > Reschedule ( 0 ) ;
2019-04-26 14:51:28 +02:00
m_ApiPackageIntegrityTimer = new Timer ( ) ;
2021-01-18 14:29:05 +01:00
m_ApiPackageIntegrityTimer - > OnTimerExpired . connect ( [ this ] ( const Timer * const & ) { CheckApiPackageIntegrity ( ) ; } ) ;
2019-04-26 14:51:28 +02:00
m_ApiPackageIntegrityTimer - > SetInterval ( 300 ) ;
m_ApiPackageIntegrityTimer - > Start ( ) ;
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
{
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > lock ( m_LogLock ) ;
2018-08-09 11:54:34 +02:00
CloseLogFile ( ) ;
2019-04-17 14:18:20 +02:00
RotateLogFile ( ) ;
2018-08-09 11:54:34 +02:00
}
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
{
2019-02-08 11:43:47 +01:00
namespace asio = boost : : asio ;
namespace ip = asio : : ip ;
using ip : : tcp ;
2014-05-03 20:02:22 +02:00
ObjectLock olock ( this ) ;
2019-11-28 19:56:15 +01:00
if ( ! m_SSLContext ) {
2014-06-05 14:36:57 +02:00
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
2019-09-09 15:11:38 +02:00
auto & io ( IoEngine : : Get ( ) . GetIoContext ( ) ) ;
2019-07-25 16:32:57 +02:00
auto acceptor ( Shared < tcp : : acceptor > : : Make ( io ) ) ;
2014-06-05 14:36:57 +02:00
try {
2019-02-08 11:43:47 +01:00
tcp : : resolver resolver ( io ) ;
tcp : : resolver : : query query ( node , service , tcp : : resolver : : query : : passive ) ;
2019-02-15 18:19:56 +01:00
auto result ( resolver . resolve ( query ) ) ;
auto current ( result . begin ( ) ) ;
for ( ; ; ) {
try {
acceptor - > open ( current - > endpoint ( ) . protocol ( ) ) ;
2019-02-21 10:55:47 +01:00
{
auto fd ( acceptor - > native_handle ( ) ) ;
const int optFalse = 0 ;
setsockopt ( fd , IPPROTO_IPV6 , IPV6_V6ONLY , reinterpret_cast < const char * > ( & optFalse ) , sizeof ( optFalse ) ) ;
const int optTrue = 1 ;
setsockopt ( fd , SOL_SOCKET , SO_REUSEADDR , reinterpret_cast < const char * > ( & optTrue ) , sizeof ( optTrue ) ) ;
2020-03-31 17:54:54 +02:00
# ifdef SO_REUSEPORT
2019-02-21 10:55:47 +01:00
setsockopt ( fd , SOL_SOCKET , SO_REUSEPORT , reinterpret_cast < const char * > ( & optTrue ) , sizeof ( optTrue ) ) ;
2020-03-31 17:54:54 +02:00
# endif /* SO_REUSEPORT */
2019-02-21 10:55:47 +01:00
}
2019-02-15 18:19:56 +01:00
acceptor - > bind ( current - > endpoint ( ) ) ;
break ;
} catch ( const std : : exception & ) {
if ( + + current = = result . end ( ) ) {
throw ;
}
if ( acceptor - > is_open ( ) ) {
acceptor - > close ( ) ;
}
}
}
2019-02-21 10:41:31 +01:00
} catch ( const std : : exception & ex ) {
2014-10-20 10:09:57 +02:00
Log ( LogCritical , " ApiListener " )
2019-06-03 18:15:02 +02:00
< < " Cannot bind TCP socket for host ' " < < node < < " ' on port ' " < < service < < " ': " < < ex . what ( ) ;
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
2019-02-08 11:43:47 +01:00
acceptor - > listen ( INT_MAX ) ;
2014-05-03 20:02:22 +02:00
2019-02-08 11:43:47 +01:00
auto localEndpoint ( acceptor - > local_endpoint ( ) ) ;
2014-08-04 16:34:17 +02:00
2019-02-08 11:43:47 +01:00
Log ( LogInformation , " ApiListener " )
< < " Started new listener on '[ " < < localEndpoint . address ( ) < < " ]: " < < localEndpoint . port ( ) < < " ' " ;
2014-05-03 20:02:22 +02:00
2019-11-25 22:10:51 +01:00
IoEngine : : SpawnCoroutine ( io , [ this , acceptor ] ( asio : : yield_context yc ) { ListenerCoroutineProc ( yc , acceptor , m_SSLContext ) ; } ) ;
2014-05-03 20:02:22 +02:00
2019-02-08 11:43:47 +01:00
UpdateStatusFile ( localEndpoint ) ;
2018-09-24 16:38:48 +02:00
2019-02-08 11:43:47 +01:00
return true ;
2014-05-03 20:02:22 +02:00
}
2019-11-25 23:37:28 +01:00
void ApiListener : : ListenerCoroutineProc ( boost : : asio : : yield_context yc , const Shared < boost : : asio : : ip : : tcp : : acceptor > : : Ptr & server , const Shared < boost : : asio : : ssl : : context > : : Ptr & sslContext )
2019-02-08 18:00:53 +01:00
{
namespace asio = boost : : asio ;
2019-09-09 15:11:38 +02:00
auto & io ( IoEngine : : Get ( ) . GetIoContext ( ) ) ;
2019-02-08 18:00:53 +01:00
2021-01-14 18:40:20 +01:00
time_t lastModified = - 1 ;
const String crlPath = GetCrlPath ( ) ;
if ( ! crlPath . IsEmpty ( ) ) {
lastModified = Utility : : GetFileCreationTime ( crlPath ) ;
}
2019-02-08 18:00:53 +01:00
for ( ; ; ) {
try {
2021-01-14 18:40:20 +01:00
asio : : ip : : tcp : : socket socket ( io ) ;
server - > async_accept ( socket . lowest_layer ( ) , yc ) ;
if ( ! crlPath . IsEmpty ( ) ) {
time_t currentCreationTime = Utility : : GetFileCreationTime ( crlPath ) ;
2019-02-18 14:23:59 +01:00
2021-01-14 18:40:20 +01:00
if ( lastModified ! = currentCreationTime ) {
UpdateSSLContext ( ) ;
lastModified = currentCreationTime ;
}
}
auto sslConn ( Shared < AsioTlsStream > : : Make ( io , * sslContext ) ) ;
sslConn - > lowest_layer ( ) = std : : move ( socket ) ;
2019-02-18 14:23:59 +01:00
2020-02-05 15:53:56 +01:00
auto strand ( Shared < asio : : io_context : : strand > : : Make ( io ) ) ;
2021-05-19 16:40:49 +02:00
IoEngine : : SpawnCoroutine ( * strand , [ this , strand , sslConn ] ( asio : : yield_context yc ) {
Timeout : : Ptr timeout ( new Timeout ( strand - > context ( ) , * strand , boost : : posix_time : : microseconds ( int64_t ( GetConnectTimeout ( ) * 1e6 ) ) ,
[ sslConn ] ( asio : : yield_context yc ) {
Log ( LogWarning , " ApiListener " )
< < " Timeout while processing incoming connection from "
< < sslConn - > lowest_layer ( ) . remote_endpoint ( ) ;
boost : : system : : error_code ec ;
sslConn - > lowest_layer ( ) . cancel ( ec ) ;
}
) ) ;
Defer cancelTimeout ( [ timeout ] ( ) { timeout - > Cancel ( ) ; } ) ;
NewClientHandler ( yc , strand , sslConn , String ( ) , RoleServer ) ;
} ) ;
2019-02-08 18:00:53 +01:00
} catch ( const std : : exception & ex ) {
2019-04-01 16:13:37 +02:00
Log ( LogCritical , " ApiListener " )
2019-06-03 18:15:02 +02:00
< < " Cannot accept new connection: " < < ex . what ( ) ;
2019-02-08 18:00:53 +01:00
}
}
}
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
{
2019-02-18 14:56:45 +01:00
namespace asio = boost : : asio ;
using asio : : ip : : tcp ;
2014-05-03 20:02:22 +02:00
2019-11-28 19:56:15 +01:00
if ( ! m_SSLContext ) {
2019-02-18 14:56:45 +01:00
Log ( LogCritical , " ApiListener " , " SSL context is required for AddConnection() " ) ;
return ;
2014-05-03 20:02:22 +02:00
}
2019-09-09 15:11:38 +02:00
auto & io ( IoEngine : : Get ( ) . GetIoContext ( ) ) ;
2020-02-05 15:53:56 +01:00
auto strand ( Shared < asio : : io_context : : strand > : : Make ( io ) ) ;
2018-09-24 16:38:48 +02:00
2020-02-05 15:53:56 +01:00
IoEngine : : SpawnCoroutine ( * strand , [ this , strand , endpoint , & io ] ( asio : : yield_context yc ) {
2019-02-18 14:56:45 +01:00
String host = endpoint - > GetHost ( ) ;
String port = endpoint - > GetPort ( ) ;
2018-09-24 16:38:48 +02:00
2018-08-09 08:29:27 +02:00
Log ( LogInformation , " ApiListener " )
2019-02-18 14:56:45 +01:00
< < " Reconnecting to endpoint ' " < < endpoint - > GetName ( ) < < " ' via host ' " < < host < < " ' and port ' " < < port < < " ' " ;
2016-08-02 12:14:03 +02:00
2014-06-05 15:03:56 +02:00
try {
2019-11-28 19:56:15 +01:00
auto sslConn ( Shared < AsioTlsStream > : : Make ( io , * m_SSLContext , endpoint - > GetName ( ) ) ) ;
2014-05-03 20:02:22 +02:00
2021-05-19 16:40:49 +02:00
Timeout : : Ptr timeout ( new Timeout ( strand - > context ( ) , * strand , boost : : posix_time : : microseconds ( int64_t ( GetConnectTimeout ( ) * 1e6 ) ) ,
[ sslConn , endpoint , host , port ] ( asio : : yield_context yc ) {
Log ( LogCritical , " ApiListener " )
< < " Timeout while reconnecting to endpoint ' " < < endpoint - > GetName ( ) < < " ' via host ' " < < host
< < " ' and port ' " < < port < < " ', cancelling attempt " ;
boost : : system : : error_code ec ;
sslConn - > lowest_layer ( ) . cancel ( ec ) ;
}
) ) ;
Defer cancelTimeout ( [ & timeout ] ( ) { timeout - > Cancel ( ) ; } ) ;
2019-02-25 16:18:48 +01:00
Connect ( sslConn - > lowest_layer ( ) , host , port , yc ) ;
2016-07-21 13:48:00 +02:00
2020-02-05 15:53:56 +01:00
NewClientHandler ( yc , strand , sslConn , endpoint - > GetName ( ) , RoleClient ) ;
2014-05-03 20:02:22 +02:00
2019-02-18 14:56:45 +01:00
endpoint - > SetConnecting ( false ) ;
Log ( LogInformation , " ApiListener " )
< < " Finished reconnecting to endpoint ' " < < endpoint - > GetName ( ) < < " ' via host ' " < < host < < " ' and port ' " < < port < < " ' " ;
} catch ( const std : : exception & ex ) {
endpoint - > SetConnecting ( false ) ;
2015-06-22 11:11:21 +02:00
2019-04-01 16:13:37 +02:00
Log ( LogCritical , " ApiListener " )
< < " Cannot connect to host ' " < < host < < " ' on port ' " < < port < < " ': " < < ex . what ( ) ;
2018-03-05 13:22:43 +01:00
}
2019-02-18 14:56:45 +01:00
} ) ;
2014-05-03 20:02:22 +02:00
}
2020-02-05 15:53:56 +01:00
void ApiListener : : NewClientHandler (
boost : : asio : : yield_context yc , const Shared < boost : : asio : : io_context : : strand > : : Ptr & strand ,
const Shared < AsioTlsStream > : : Ptr & client , const String & hostname , ConnectionRole role
)
2019-02-08 18:00:53 +01:00
{
try {
2020-02-05 15:53:56 +01:00
NewClientHandlerInternal ( yc , strand , client , hostname , role ) ;
2019-02-08 18:00:53 +01:00
} catch ( const std : : exception & ex ) {
Log ( LogCritical , " ApiListener " )
< < " Exception while handling new API client connection: " < < DiagnosticInformation ( ex , false ) ;
Log ( LogDebug , " ApiListener " )
< < " Exception while handling new API client connection: " < < DiagnosticInformation ( ex ) ;
}
}
2020-04-03 11:10:08 +02:00
static const auto l_AppVersionInt ( ( [ ] ( ) - > unsigned long {
auto appVersion ( Application : : GetAppVersion ( ) ) ;
2021-02-25 15:31:07 +01:00
boost : : regex rgx ( R " EOF(^[rv]?( \ d+) \ .( \ d+) \ .( \ d+))EOF " ) ;
2020-04-03 11:10:08 +02:00
boost : : smatch match ;
if ( ! boost : : regex_search ( appVersion . GetData ( ) , match , rgx ) ) {
return 0 ;
}
return 100u * 100u * boost : : lexical_cast < unsigned long > ( match [ 1 ] . str ( ) )
+ 100u * boost : : lexical_cast < unsigned long > ( match [ 2 ] . str ( ) )
+ boost : : lexical_cast < unsigned long > ( match [ 3 ] . str ( ) ) ;
} ) ( ) ) ;
2020-10-19 12:31:57 +02:00
static const auto l_MyCapabilities ( ApiCapabilities : : ExecuteArbitraryCommand ) ;
2019-02-08 18:00:53 +01:00
/**
* Processes a new client connection .
*
* @ param client The new client .
*/
2020-02-05 15:53:56 +01:00
void ApiListener : : NewClientHandlerInternal (
boost : : asio : : yield_context yc , const Shared < boost : : asio : : io_context : : strand > : : Ptr & strand ,
const Shared < AsioTlsStream > : : Ptr & client , const String & hostname , ConnectionRole role
)
2019-02-08 18:00:53 +01:00
{
2019-02-12 14:56:47 +01:00
namespace asio = boost : : asio ;
namespace ssl = asio : : ssl ;
2019-02-08 18:00:53 +01:00
String conninfo ;
{
std : : ostringstream conninfo_ ;
if ( role = = RoleClient ) {
conninfo_ < < " to " ;
} else {
conninfo_ < < " from " ;
}
auto endpoint ( client - > lowest_layer ( ) . remote_endpoint ( ) ) ;
conninfo_ < < " [ " < < endpoint . address ( ) < < " ]: " < < endpoint . port ( ) ;
conninfo = conninfo_ . str ( ) ;
}
2019-02-12 14:56:47 +01:00
auto & sslConn ( client - > next_layer ( ) ) ;
2019-06-03 18:15:02 +02:00
boost : : system : : error_code ec ;
2020-02-05 15:53:56 +01:00
{
2020-02-05 17:17:41 +01:00
Timeout : : Ptr handshakeTimeout ( new Timeout (
strand - > context ( ) ,
* strand ,
boost : : posix_time : : microseconds ( intmax_t ( Configuration : : TlsHandshakeTimeout * 1000000 ) ) ,
[ strand , client ] ( asio : : yield_context yc ) {
boost : : system : : error_code ec ;
2020-02-05 15:53:56 +01:00
client - > lowest_layer ( ) . cancel ( ec ) ;
}
2020-02-05 17:17:41 +01:00
) ) ;
2020-02-05 15:53:56 +01:00
sslConn . async_handshake ( role = = RoleClient ? sslConn . client : sslConn . server , yc [ ec ] ) ;
2020-02-05 17:17:41 +01:00
handshakeTimeout - > Cancel ( ) ;
2020-02-05 15:53:56 +01:00
}
2019-06-03 18:15:02 +02:00
if ( ec ) {
// https://github.com/boostorg/beast/issues/915
// Google Chrome 73+ seems not close the connection properly, https://stackoverflow.com/questions/56272906/how-to-fix-certificate-unknown-error-from-chrome-v73
if ( ec = = asio : : ssl : : error : : stream_truncated ) {
Log ( LogNotice , " ApiListener " )
< < " TLS stream was truncated, ignoring connection from " < < conninfo ;
return ;
}
2019-02-08 18:00:53 +01:00
Log ( LogCritical , " ApiListener " )
2019-06-03 18:15:02 +02:00
< < " Client TLS handshake failed ( " < < conninfo < < " ): " < < ec . message ( ) ;
2019-02-12 14:56:25 +01:00
return ;
}
2019-02-19 18:06:14 +01:00
bool willBeShutDown = false ;
Defer shutDownIfNeeded ( [ & sslConn , & willBeShutDown , & yc ] ( ) {
if ( ! willBeShutDown ) {
2019-09-09 10:53:37 +02:00
// Ignore the error, but do not throw an exception being swallowed at all cost.
// https://github.com/Icinga/icinga2/issues/7351
boost : : system : : error_code ec ;
sslConn . async_shutdown ( yc [ ec ] ) ;
2019-02-19 18:06:14 +01:00
}
} ) ;
2019-02-25 17:22:00 +01:00
std : : shared_ptr < X509 > cert ( sslConn . GetPeerCertificate ( ) ) ;
2019-02-25 16:18:48 +01:00
bool verify_ok = false ;
2019-02-12 14:56:25 +01:00
String identity ;
Endpoint : : Ptr endpoint ;
if ( cert ) {
2019-02-25 16:18:48 +01:00
verify_ok = sslConn . IsVerifyOK ( ) ;
String verifyError = sslConn . GetVerifyError ( ) ;
2019-02-12 14:56:25 +01:00
try {
identity = GetCertificateCN ( cert ) ;
} catch ( const std : : exception & ) {
Log ( LogCritical , " ApiListener " )
2021-10-08 10:27:35 +02:00
< < " Cannot get certificate common name from peer ( " < < conninfo < < " ) cert. " ;
2019-02-12 14:56:25 +01:00
return ;
}
if ( ! hostname . IsEmpty ( ) ) {
if ( identity ! = hostname ) {
Log ( LogWarning , " ApiListener " )
< < " Unexpected certificate common name while connecting to endpoint ' "
< < hostname < < " ': got ' " < < identity < < " ' " ;
return ;
} else if ( ! verify_ok ) {
Log ( LogWarning , " ApiListener " )
< < " Certificate validation failed for endpoint ' " < < hostname
< < " ': " < < verifyError ;
}
}
if ( verify_ok ) {
endpoint = Endpoint : : GetByName ( identity ) ;
}
Log log ( LogInformation , " ApiListener " ) ;
log < < " New client connection for identity ' " < < identity < < " ' " < < conninfo ;
if ( ! verify_ok ) {
log < < " (certificate validation failed: " < < verifyError < < " ) " ;
} else if ( ! endpoint ) {
log < < " (no Endpoint object found for identity) " ;
}
} else {
Log ( LogInformation , " ApiListener " )
< < " New client connection " < < conninfo < < " (no client certificate) " ;
2019-02-08 18:00:53 +01:00
}
2019-02-12 14:56:47 +01:00
ClientType ctype ;
2019-02-18 15:31:58 +01:00
if ( role = = RoleClient ) {
JsonRpc : : SendMessage ( client , new Dictionary ( {
{ " jsonrpc " , " 2.0 " } ,
{ " method " , " icinga::Hello " } ,
2020-04-03 11:10:08 +02:00
{ " params " , new Dictionary ( {
2020-10-19 12:31:57 +02:00
{ " version " , ( double ) l_AppVersionInt } ,
{ " capabilities " , ( double ) l_MyCapabilities }
2020-04-03 11:10:08 +02:00
} ) }
2019-02-18 15:31:58 +01:00
} ) , yc ) ;
client - > async_flush ( yc ) ;
ctype = ClientJsonRpc ;
} else {
2019-02-12 14:56:47 +01:00
{
boost : : system : : error_code ec ;
if ( client - > async_fill ( yc [ ec ] ) = = 0u ) {
if ( identity . IsEmpty ( ) ) {
Log ( LogInformation , " ApiListener " )
2019-06-03 18:15:02 +02:00
< < " No data received on new API connection " < < conninfo < < " . "
2019-02-12 14:56:47 +01:00
< < " Ensure that the remote endpoints are properly configured in a cluster setup. " ;
} else {
Log ( LogWarning , " ApiListener " )
2019-06-03 18:15:02 +02:00
< < " No data received on new API connection " < < conninfo < < " for identity ' " < < identity < < " '. "
2019-02-12 14:56:47 +01:00
< < " Ensure that the remote endpoints are properly configured in a cluster setup. " ;
}
return ;
}
}
char firstByte = 0 ;
{
asio : : mutable_buffer firstByteBuf ( & firstByte , 1 ) ;
client - > peek ( firstByteBuf ) ;
}
if ( firstByte > = ' 0 ' & & firstByte < = ' 9 ' ) {
2020-04-03 11:10:08 +02:00
JsonRpc : : SendMessage ( client , new Dictionary ( {
{ " jsonrpc " , " 2.0 " } ,
{ " method " , " icinga::Hello " } ,
{ " params " , new Dictionary ( {
2020-10-19 12:31:57 +02:00
{ " version " , ( double ) l_AppVersionInt } ,
{ " capabilities " , ( double ) l_MyCapabilities }
2020-04-03 11:10:08 +02:00
} ) }
} ) , yc ) ;
client - > async_flush ( yc ) ;
2019-02-12 14:56:47 +01:00
ctype = ClientJsonRpc ;
} else {
ctype = ClientHttp ;
}
}
2019-02-14 16:10:41 +01:00
2019-02-19 13:57:36 +01:00
if ( ctype = = ClientJsonRpc ) {
Log ( LogNotice , " ApiListener " , " New JSON-RPC client " ) ;
2020-10-26 15:20:58 +01:00
if ( endpoint & & endpoint - > GetConnected ( ) ) {
Log ( LogNotice , " ApiListener " )
< < " Ignoring JSON-RPC connection " < < conninfo
< < " . We're already connected to Endpoint ' " < < endpoint - > GetName ( ) < < " '. " ;
return ;
}
2019-02-19 13:57:36 +01:00
JsonRpcConnection : : Ptr aclient = new JsonRpcConnection ( identity , verify_ok , client , role ) ;
if ( endpoint ) {
endpoint - > AddClient ( aclient ) ;
2020-11-12 13:46:47 +01:00
Utility : : QueueAsyncCallback ( [ this , aclient , endpoint ] ( ) {
2020-10-26 15:20:58 +01:00
SyncClient ( aclient , endpoint , true ) ;
2019-02-19 13:57:36 +01:00
} ) ;
} else if ( ! AddAnonymousClient ( aclient ) ) {
Log ( LogNotice , " ApiListener " )
< < " Ignoring anonymous JSON-RPC connection " < < conninfo
< < " . Max connections ( " < < GetMaxAnonymousClients ( ) < < " ) exceeded. " ;
aclient = nullptr ;
}
if ( aclient ) {
aclient - > Start ( ) ;
2019-02-19 18:06:14 +01:00
willBeShutDown = true ;
2019-02-19 13:57:36 +01:00
}
} else {
2019-02-14 16:10:41 +01:00
Log ( LogNotice , " ApiListener " , " New HTTP client " ) ;
HttpServerConnection : : Ptr aclient = new HttpServerConnection ( identity , verify_ok , client ) ;
AddHttpClient ( aclient ) ;
aclient - > Start ( ) ;
2019-02-19 18:06:14 +01:00
willBeShutDown = true ;
2019-02-14 16:10:41 +01:00
}
2019-02-08 18:00:53 +01: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
2021-01-18 14:29:05 +01:00
if ( Utility : : PathExists ( ApiListener : : GetCertificateRequestsDir ( ) ) ) {
Utility : : Glob ( ApiListener : : GetCertificateRequestsDir ( ) + " /*.json " , [ aclient ] ( const String & newPath ) {
JsonRpcConnection : : SendCertificateRequest ( aclient , nullptr , newPath ) ;
} , 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 ;
2021-01-18 14:29:05 +01:00
Utility : : Glob ( GetApiDir ( ) + " log/* " , [ & files ] ( const String & file ) { LogGlobHandler ( files , file ) ; } , 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 ;
2019-04-17 13:52:13 +02:00
auto localZone ( GetLocalEndpoint ( ) - > GetZone ( ) ) ;
2014-05-03 20:02:22 +02:00
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 ;
2019-04-17 13:52:13 +02:00
auto zone ( endpoint - > GetZone ( ) ) ;
/* only care for endpoints in a) the same zone b) our parent zone c) immediate child zones */
if ( ! ( zone = = localZone | | zone = = localZone - > GetParent ( ) | | zone - > GetParent ( ) = = localZone ) ) {
continue ;
}
2014-05-03 20:02:22 +02:00
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 ( ) ) {
2019-02-19 13:57:36 +01:00
if ( client - > GetTimestamp ( ) = = maxTs ) {
2016-07-21 12:27:18 +02:00
client - > SendMessage ( lmessage ) ;
2019-02-20 12:00:11 +01:00
} else {
client - > Disconnect ( ) ;
2019-02-19 13:57:36 +01:00
}
2016-07-21 12:27:18 +02:00
}
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 ) ;
2019-02-18 14:56:45 +01:00
AddConnection ( endpoint ) ;
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 ;
2021-01-18 14:29:05 +01:00
Utility : : Glob ( requestsDir + " /*.json " , [ expiryTime ] ( const String & path ) {
CleanupCertificateRequest ( path , 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 ;
2021-01-18 14:29:05 +01:00
m_RelayQueue . Enqueue ( [ this , origin , secobj , message , log ] ( ) { SyncRelayMessage ( 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
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > lock ( m_LogLock ) ;
2014-05-03 20:02:22 +02:00
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
}
}
2020-11-05 12:10:29 +01:00
/**
* Relay a message to a directly connected zone or to a global zone .
* If some other zone is passed as the target zone , it is not relayed .
*
* @ param targetZone The zone to relay to
* @ param origin Information about where this message is relayed from ( if it was not generated locally )
* @ param message The message to relay
* @ param currentZoneMaster The current master node of the local zone
* @ return true if the message has been relayed to all relevant endpoints ,
* false if it hasn ' t and must be persisted in the replay log
*/
2019-04-10 14:17:36 +02:00
bool ApiListener : : RelayMessageOne ( const Zone : : Ptr & targetZone , const MessageOrigin : : Ptr & origin , const Dictionary : : Ptr & message , const Endpoint : : Ptr & currentZoneMaster )
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
2019-04-10 14:17:36 +02:00
Zone : : Ptr localZone = Zone : : GetLocalZone ( ) ;
2016-01-27 12:18:16 +01:00
2019-04-10 14:17:36 +02:00
/* only relay the message to a) the same local zone, b) the parent zone and c) direct child zones. Exception is a global zone. */
2016-11-11 10:41:49 +01:00
if ( ! targetZone - > GetGlobal ( ) & &
2019-04-10 14:17:36 +02:00
targetZone ! = localZone & &
targetZone ! = localZone - > GetParent ( ) & &
targetZone - > GetParent ( ) ! = localZone ) {
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
2019-04-10 14:17:36 +02:00
Endpoint : : Ptr localEndpoint = GetLocalEndpoint ( ) ;
2016-01-27 13:39:31 +01:00
2014-05-03 20:02:22 +02:00
std : : vector < Endpoint : : Ptr > skippedEndpoints ;
2020-11-05 12:10:29 +01:00
std : : set < Zone : : Ptr > allTargetZones ;
2016-11-11 10:41:49 +01:00
if ( targetZone - > GetGlobal ( ) ) {
2020-11-05 12:10:29 +01:00
/* if the zone is global, the message has to be relayed to our local zone and direct children */
allTargetZones . insert ( localZone ) ;
2016-11-11 10:41:49 +01:00
for ( const Zone : : Ptr & zone : ConfigType : : GetObjectsByType < Zone > ( ) ) {
2019-04-10 14:17:36 +02:00
if ( zone - > GetParent ( ) = = localZone ) {
2020-11-05 12:10:29 +01:00
allTargetZones . insert ( zone ) ;
2016-11-11 10:41:49 +01:00
}
}
} else {
2020-11-05 12:10:29 +01:00
/* whereas if it's not global, the message is just relayed to the zone itself */
allTargetZones . insert ( targetZone ) ;
2016-11-11 10:41:49 +01:00
}
2020-11-05 12:10:29 +01:00
bool needsReplay = false ;
2014-05-03 20:02:22 +02:00
2020-11-05 12:10:29 +01:00
for ( const Zone : : Ptr & currentTargetZone : allTargetZones ) {
bool relayed = false , log_needed = false , log_done = false ;
2015-10-01 09:17:23 +02:00
2020-11-05 12:10:29 +01:00
for ( const Endpoint : : Ptr & targetEndpoint : currentTargetZone - > GetEndpoints ( ) ) {
/* Don't relay messages to ourselves. */
if ( targetEndpoint = = localEndpoint )
continue ;
2015-10-15 09:28:20 +02:00
2020-11-05 12:10:29 +01:00
log_needed = true ;
2015-10-01 09:17:23 +02:00
2020-11-05 12:10:29 +01:00
/* Don't relay messages to disconnected endpoints. */
if ( ! targetEndpoint - > GetConnected ( ) ) {
if ( currentTargetZone = = localZone )
log_done = false ;
2015-10-01 09:17:23 +02:00
2020-11-05 12:10:29 +01:00
continue ;
}
2014-05-03 20:02:22 +02:00
2020-11-05 12:10:29 +01:00
log_done = true ;
2014-05-03 20:02:22 +02:00
2020-11-05 12:10:29 +01:00
/* Don't relay the message to the zone through more than one endpoint unless this is our own zone.
* ' relayed ' is set to true on success below , enabling the checks in the second iteration .
*/
if ( relayed & & currentTargetZone ! = localZone ) {
skippedEndpoints . push_back ( targetEndpoint ) ;
continue ;
}
2014-05-03 20:02:22 +02:00
2020-11-05 12:10:29 +01:00
/* Don't relay messages back to the endpoint which we got the message from. */
if ( origin & & origin - > FromClient & & targetEndpoint = = origin - > FromClient - > GetEndpoint ( ) ) {
skippedEndpoints . push_back ( targetEndpoint ) ;
continue ;
}
2019-04-10 14:17:36 +02:00
2020-11-05 12:10:29 +01:00
/* Don't relay messages back to the zone which we got the message from. */
if ( origin & & origin - > FromZone & & currentTargetZone = = origin - > FromZone ) {
skippedEndpoints . push_back ( targetEndpoint ) ;
continue ;
}
/* Only relay message to the zone master if we're not currently the zone master.
* e1 is zone master , e2 and e3 are zone members .
*
* Message is sent from e2 or e3 :
* ! isMaster = = true
* targetEndpoint e1 is zone master - > send the message
* targetEndpoint e3 is not zone master - > skip it , avoid routing loops
*
* Message is sent from e1 :
* ! isMaster = = false - > send the messages to e2 and e3 being the zone routing master .
*/
bool isMaster = ( currentZoneMaster = = localEndpoint ) ;
if ( ! isMaster & & targetEndpoint ! = currentZoneMaster ) {
skippedEndpoints . push_back ( targetEndpoint ) ;
continue ;
}
relayed = true ;
2014-05-03 20:02:22 +02:00
2020-11-05 12:10:29 +01:00
SyncSendMessage ( targetEndpoint , message ) ;
}
2014-05-03 20:02:22 +02:00
2020-11-05 12:10:29 +01:00
if ( log_needed & & ! log_done ) {
needsReplay = true ;
}
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
2019-04-10 14:17:36 +02:00
for ( const Endpoint : : Ptr & skippedEndpoint : skippedEndpoints )
skippedEndpoint - > SetLocalLogPosition ( ts ) ;
2016-01-27 12:18:16 +01:00
}
2020-11-05 12:10:29 +01:00
return ! needsReplay ;
2016-01-27 12:18:16 +01:00
}
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
2019-04-17 14:31:49 +02:00
// If the log is being rotated more than once per second,
// don't overwrite the previous one, but silently deny rotation.
if ( ! Utility : : PathExists ( newpath ) ) {
2020-01-20 11:25:16 +01:00
try {
Utility : : RenameFile ( oldpath , newpath ) ;
} catch ( const std : : exception & ex ) {
Log ( LogCritical , " ApiListener " )
< < " Cannot rotate replay log file from ' " < < oldpath < < " ' to ' "
< < newpath < < " ': " < < ex . what ( ) ;
}
2019-04-17 14:31:49 +02:00
}
2014-05-03 20:02:22 +02:00
}
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 ( ; ; ) {
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > lock ( m_LogLock ) ;
2014-05-03 20:02:22 +02:00
CloseLogFile ( ) ;
if ( count = = - 1 | | count > 50000 ) {
OpenLogFile ( ) ;
lock . unlock ( ) ;
} else {
last_sync = true ;
}
count = 0 ;
std : : vector < int > files ;
2021-01-18 14:29:05 +01:00
Utility : : Glob ( GetApiDir ( ) + " log/* " , [ & files ] ( const String & file ) { LogGlobHandler ( files , file ) ; } , GlobFile ) ;
2014-05-03 20:02:22 +02:00
std : : sort ( files . begin ( ) , files . end ( ) ) ;
2019-04-17 15:42:39 +02:00
std : : vector < std : : pair < int , String > > allFiles ;
2016-08-25 06:19:44 +02:00
for ( int ts : files ) {
2019-04-17 15:42:39 +02:00
if ( ts > = peer_ts ) {
allFiles . emplace_back ( ts , GetApiDir ( ) + " log/ " + Convert : : ToString ( ts ) ) ;
}
}
2014-05-03 20:02:22 +02:00
2019-04-17 15:42:39 +02:00
allFiles . emplace_back ( Utility : : GetTime ( ) + 1 , GetApiDir ( ) + " log/current " ) ;
2014-05-03 20:02:22 +02:00
2019-04-17 15:42:39 +02:00
for ( auto & file : allFiles ) {
2014-10-20 10:09:57 +02:00
Log ( LogNotice , " ApiListener " )
2019-04-17 15:42:39 +02:00
< < " Replaying log: " < < file . second ;
2014-05-03 20:02:22 +02:00
2019-04-17 15:42:39 +02:00
auto * fp = new std : : fstream ( file . second . 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 " )
2019-04-17 15:42:39 +02:00
< < " Unexpected end-of-file for cluster log: " < < file . second ;
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 {
2019-02-26 11:13:34 +01:00
client - > SendRawMessage ( pmessage - > Get ( " message " ) ) ;
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
2019-04-17 15:42:39 +02:00
if ( file . first > logpos_ts + 10 ) {
logpos_ts = file . first ;
2015-02-26 14:59:39 +01:00
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
2019-02-19 13:57:36 +01:00
client - > SendMessage ( lmessage ) ;
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
}
2019-02-11 13:54:17 +01:00
else {
Log ( LogNotice , " ApiListener " )
< < " 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 syncQueueItems = m_SyncQueue . GetLength ( ) ;
size_t relayQueueItems = m_RelayQueue . GetLength ( ) ;
2019-02-20 14:24:09 +01:00
double workQueueItemRate = JsonRpcConnection : : GetWorkQueueRate ( ) ;
2017-05-11 17:30:20 +02:00
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
{ " sync_queue_items " , syncQueueItems } ,
{ " relay_queue_items " , relayQueueItems } ,
2019-02-20 14:24:09 +01:00
{ " work_queue_item_rate " , workQueueItemRate } ,
2018-01-11 11:17:38 +01:00
{ " 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_sync_queue_items " , syncQueueItems ) ;
perfdata - > Set ( " num_json_rpc_relay_queue_items " , relayQueueItems ) ;
2019-02-20 14:24:09 +01:00
perfdata - > Set ( " num_json_rpc_work_queue_item_rate " , workQueueItemRate ) ;
2017-05-11 17:30:20 +02:00
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
{
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > lock ( m_AnonymousClientsLock ) ;
2018-09-05 17:45:35 +02:00
2019-03-08 14:07:29 +01:00
if ( GetMaxAnonymousClients ( ) > = 0 & & ( long ) m_AnonymousClients . size ( ) + 1 > ( long ) 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
{
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > 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
{
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > 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
{
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > 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
{
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > 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
{
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > lock ( m_HttpClientsLock ) ;
2015-06-22 11:11:21 +02:00
return m_HttpClients ;
}
2015-08-11 14:13:53 +02:00
2020-07-06 17:30:49 +02:00
static void LogAppVersion ( unsigned long version , Log & log )
{
log < < version / 100u < < " . " < < version % 100u < < " .x " ;
}
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
{
2020-04-03 11:10:08 +02:00
if ( origin ) {
auto client ( origin - > FromClient ) ;
if ( client ) {
auto endpoint ( client - > GetEndpoint ( ) ) ;
if ( endpoint ) {
2020-07-06 17:30:49 +02:00
unsigned long nodeVersion = params - > Get ( " version " ) ;
endpoint - > SetIcingaVersion ( nodeVersion ) ;
2020-10-19 12:31:57 +02:00
endpoint - > SetCapabilities ( ( double ) params - > Get ( " capabilities " ) ) ;
2020-07-06 17:30:49 +02:00
if ( nodeVersion = = 0u ) {
nodeVersion = 21200 ;
}
if ( endpoint - > GetZone ( ) - > GetParent ( ) = = Zone : : GetLocalZone ( ) ) {
switch ( l_AppVersionInt / 100 - nodeVersion / 100 ) {
case 0 :
case 1 :
break ;
default :
Log log ( LogWarning , " ApiListener " ) ;
log < < " Unexpected Icinga version of endpoint ' " < < endpoint - > GetName ( ) < < " ': " ;
LogAppVersion ( nodeVersion / 100u , log ) ;
log < < " Expected one of: " ;
LogAppVersion ( l_AppVersionInt / 100u , log ) ;
log < < " , " ;
LogAppVersion ( ( l_AppVersionInt / 100u - 1u ) , log ) ;
}
}
2020-04-03 11:10:08 +02:00
}
}
}
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
2019-04-16 16:37:38 +02:00
void ApiListener : : UpdateActivePackageStagesCache ( )
{
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > lock ( m_ActivePackageStagesLock ) ;
2019-04-16 16:37:38 +02:00
for ( auto package : ConfigPackageUtility : : GetPackages ( ) ) {
String activeStage ;
try {
activeStage = ConfigPackageUtility : : GetActiveStageFromFile ( package ) ;
} catch ( const std : : exception & ex ) {
Log ( LogCritical , " ApiListener " )
< < ex . what ( ) ;
continue ;
}
Log ( LogNotice , " ApiListener " )
< < " Updating cache: Config package ' " < < package < < " ' has active stage ' " < < activeStage < < " '. " ;
m_ActivePackageStages [ package ] = activeStage ;
}
}
2019-04-26 14:51:28 +02:00
void ApiListener : : CheckApiPackageIntegrity ( )
{
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > lock ( m_ActivePackageStagesLock ) ;
2019-04-26 14:51:28 +02:00
for ( auto package : ConfigPackageUtility : : GetPackages ( ) ) {
String activeStage ;
try {
activeStage = ConfigPackageUtility : : GetActiveStageFromFile ( package ) ;
} catch ( const std : : exception & ex ) {
/* An error means that the stage is broken, try to repair it. */
2019-04-30 12:19:35 +02:00
auto it = m_ActivePackageStages . find ( package ) ;
if ( it = = m_ActivePackageStages . end ( ) )
continue ;
String activeStageCached = it - > second ;
2019-04-26 14:51:28 +02:00
Log ( LogInformation , " ApiListener " )
< < " Repairing broken API config package ' " < < package
< < " ', setting active stage ' " < < activeStageCached < < " '. " ;
ConfigPackageUtility : : SetActiveStageToFile ( package , activeStageCached ) ;
}
}
}
2019-04-16 16:37:38 +02:00
void ApiListener : : SetActivePackageStage ( const String & package , const String & stage )
{
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > lock ( m_ActivePackageStagesLock ) ;
2019-04-16 16:37:38 +02:00
m_ActivePackageStages [ package ] = stage ;
}
String ApiListener : : GetActivePackageStage ( const String & package )
{
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > lock ( m_ActivePackageStagesLock ) ;
2019-04-16 16:37:38 +02:00
if ( m_ActivePackageStages . find ( package ) = = m_ActivePackageStages . end ( ) )
BOOST_THROW_EXCEPTION ( ScriptError ( " Package " + package + " has no active stage. " ) ) ;
return m_ActivePackageStages [ package ] ;
}
void ApiListener : : RemoveActivePackageStage ( const String & package )
{
/* This is the rare occassion when a package has been deleted. */
2021-02-02 10:16:04 +01:00
std : : unique_lock < std : : mutex > lock ( m_ActivePackageStagesLock ) ;
2019-04-16 16:37:38 +02:00
auto it = m_ActivePackageStages . find ( package ) ;
if ( it = = m_ActivePackageStages . end ( ) )
return ;
m_ActivePackageStages . erase ( it ) ;
}
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
2021-04-14 15:35:54 +02:00
try {
ResolveTlsProtocolVersion ( lvalue ( ) ) ;
} catch ( const std : : exception & ex ) {
BOOST_THROW_EXCEPTION ( ValidationError ( this , { " tls_protocolmin " } , ex . what ( ) ) ) ;
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
2019-02-08 11:43:47 +01:00
void ApiListener : : UpdateStatusFile ( boost : : asio : : ip : : tcp : : endpoint localEndpoint )
2018-08-02 08:38:42 +02:00
{
2018-08-09 15:37:23 +02:00
String path = Configuration : : CacheDir + " /api-state.json " ;
2018-08-02 08:38:42 +02:00
Utility : : SaveJsonFile ( path , 0644 , new Dictionary ( {
2019-02-08 11:43:47 +01:00
{ " host " , String ( localEndpoint . address ( ) . to_string ( ) ) } ,
{ " port " , localEndpoint . port ( ) }
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
2019-04-10 14:16:39 +02:00
Utility : : Remove ( path ) ;
2018-08-09 11:54:34 +02:00
}