2019-02-25 14:48:22 +01:00
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2012-05-10 12:06:41 +02:00
2014-05-25 16:23:35 +02:00
# include "base/application.hpp"
2018-01-18 13:50:38 +01:00
# include "base/application-ti.cpp"
2014-05-25 16:23:35 +02:00
# include "base/stacktrace.hpp"
# include "base/timer.hpp"
2014-08-07 08:34:38 +02:00
# include "base/logger.hpp"
2014-05-25 16:23:35 +02:00
# include "base/exception.hpp"
# include "base/objectlock.hpp"
# include "base/utility.hpp"
2015-03-18 13:24:31 +01:00
# include "base/loader.hpp"
2014-05-25 16:23:35 +02:00
# include "base/debug.hpp"
# include "base/type.hpp"
# include "base/convert.hpp"
2014-12-14 11:33:45 +01:00
# include "base/scriptglobal.hpp"
2014-05-25 16:23:35 +02:00
# include "base/process.hpp"
2020-02-13 15:25:03 +01:00
# include "base/tlsutility.hpp"
2015-04-27 16:21:59 +02:00
# include <boost/algorithm/string/trim.hpp>
2013-03-18 17:04:22 +01:00
# include <boost/exception/errinfo_api_function.hpp>
# include <boost/exception/errinfo_errno.hpp>
# include <boost/exception/errinfo_file_name.hpp>
2014-12-20 15:29:04 +01:00
# include <sstream>
2013-03-17 22:14:40 +01:00
# include <iostream>
2014-12-20 15:29:04 +01:00
# include <fstream>
2017-11-21 12:12:58 +01:00
# include <thread>
2015-03-03 13:53:11 +01:00
# ifdef __linux__
# include <sys/prctl.h>
# endif /* __linux__ */
2017-08-22 10:26:23 +02:00
# ifdef _WIN32
# include <windows.h>
2019-07-15 13:59:17 +02:00
# else /* _WIN32 */
# include <signal.h>
2018-01-17 13:52:23 +01:00
# endif /* _WIN32 */
2017-08-22 10:26:23 +02:00
2012-03-28 13:24:49 +02:00
using namespace icinga ;
2013-11-08 16:07:21 +01:00
REGISTER_TYPE ( Application ) ;
2013-11-08 11:17:46 +01:00
2018-01-04 04:25:35 +01:00
boost : : signals2 : : signal < void ( ) > Application : : OnReopenLogs ;
2017-12-14 15:37:20 +01:00
Application : : Ptr Application : : m_Instance = nullptr ;
2012-07-10 12:21:19 +02:00
bool Application : : m_ShuttingDown = false ;
2014-04-27 21:47:25 +02:00
bool Application : : m_RequestRestart = false ;
2014-05-22 11:22:30 +02:00
bool Application : : m_RequestReopenLogs = false ;
2014-05-17 23:07:10 +02:00
pid_t Application : : m_ReloadProcess = 0 ;
2019-07-15 13:59:17 +02:00
# ifndef _WIN32
pid_t Application : : m_UmbrellaProcess = 0 ;
# endif /* _WIN32 */
2014-05-11 17:14:35 +02:00
static bool l_Restarting = false ;
2014-09-09 14:49:21 +02:00
static bool l_InExceptionHandler = false ;
2013-02-14 16:18:58 +01:00
int Application : : m_ArgC ;
char * * Application : : m_ArgV ;
2013-10-26 09:41:45 +02:00
double Application : : m_StartTime ;
2015-11-05 14:29:45 +01:00
bool Application : : m_ScriptDebuggerEnabled = false ;
2016-05-11 16:07:28 +02:00
double Application : : m_LastReloadFailed ;
2012-03-28 13:24:49 +02:00
2012-04-24 14:02:15 +02:00
/**
* Constructor for the Application class .
*/
2018-01-04 04:25:35 +01:00
void Application : : OnConfigLoaded ( )
2012-03-28 13:24:49 +02:00
{
2017-12-14 15:37:20 +01:00
m_PidFile = nullptr ;
2012-08-14 12:51:51 +02:00
2017-12-14 15:37:20 +01:00
ASSERT ( m_Instance = = nullptr ) ;
2012-08-14 12:51:51 +02:00
m_Instance = this ;
2012-03-28 13:24:49 +02:00
}
2012-04-24 14:02:15 +02:00
/**
* Destructor for the application class .
*/
2015-08-20 17:18:48 +02:00
void Application : : Stop ( bool runtimeRemoved )
2012-03-28 13:24:49 +02:00
{
2012-05-25 22:04:03 +02:00
m_ShuttingDown = true ;
2012-04-01 15:20:13 +02:00
# ifdef _WIN32
WSACleanup ( ) ;
# endif /* _WIN32 */
2012-07-12 17:03:34 +02:00
2019-07-15 16:08:08 +02:00
# ifdef _WIN32
2018-03-14 10:01:11 +01:00
ClosePidFile ( true ) ;
2019-07-15 16:08:08 +02:00
# endif /* _WIN32 */
2014-04-16 15:01:31 +02:00
2015-08-20 17:18:48 +02:00
ObjectImpl < Application > : : Stop ( runtimeRemoved ) ;
2012-03-28 13:24:49 +02:00
}
2018-01-04 04:25:35 +01:00
Application : : ~ Application ( )
2013-08-20 11:06:04 +02:00
{
2017-12-14 15:37:20 +01:00
m_Instance = nullptr ;
2013-08-20 11:06:04 +02:00
}
2014-08-04 16:43:34 +02:00
void Application : : Exit ( int rc )
2014-08-04 16:26:17 +02:00
{
2014-08-05 13:52:24 +02:00
std : : cout . flush ( ) ;
2015-03-20 12:27:40 +01:00
std : : cerr . flush ( ) ;
2014-08-07 08:34:38 +02:00
2016-08-25 06:19:44 +02:00
for ( const Logger : : Ptr & logger : Logger : : GetLoggers ( ) ) {
2014-08-07 08:34:38 +02:00
logger - > Flush ( ) ;
}
2014-12-04 21:45:15 +01:00
UninitializeBase ( ) ;
2014-12-19 12:19:28 +01:00
# ifdef I2_DEBUG
2014-11-12 09:33:13 +01:00
exit ( rc ) ;
2014-12-19 12:19:28 +01:00
# else /* I2_DEBUG */
2014-08-04 16:43:34 +02:00
_exit ( rc ) ; // Yay, our static destructors are pretty much beyond repair at this point.
2014-12-19 12:19:28 +01:00
# endif /* I2_DEBUG */
2014-08-04 16:26:17 +02:00
}
2018-01-04 04:25:35 +01:00
void Application : : InitializeBase ( )
2014-05-03 20:02:22 +02:00
{
2014-10-21 16:07:22 +02:00
# ifdef _WIN32
/* disable GUI-based error messages for LoadLibrary() */
SetErrorMode ( SEM_FAILCRITICALERRORS ) ;
WSADATA wsaData ;
if ( WSAStartup ( MAKEWORD ( 1 , 1 ) , & wsaData ) ! = 0 ) {
BOOST_THROW_EXCEPTION ( win32_error ( )
< < boost : : errinfo_api_function ( " WSAStartup " )
< < errinfo_win32_error ( WSAGetLastError ( ) ) ) ;
}
2017-01-12 10:50:04 +01:00
# else /* _WIN32 */
struct sigaction sa ;
memset ( & sa , 0 , sizeof ( sa ) ) ;
sa . sa_handler = SIG_IGN ;
2017-12-14 15:37:20 +01:00
sigaction ( SIGPIPE , & sa , nullptr ) ;
2014-10-21 16:07:22 +02:00
# endif /* _WIN32 */
2015-03-18 13:24:31 +01:00
Loader : : ExecuteDeferredInitializers ( ) ;
2014-12-04 21:45:15 +01:00
2018-07-19 13:34:12 +02:00
/* Make sure the thread pool gets initialized. */
2015-10-21 07:02:49 +02:00
GetTP ( ) . Start ( ) ;
2018-07-19 12:49:27 +02:00
2018-07-19 13:34:12 +02:00
/* Make sure the timer thread gets initialized. */
2018-07-19 12:49:27 +02:00
Timer : : Initialize ( ) ;
2014-12-04 21:45:15 +01:00
}
2018-01-04 04:25:35 +01:00
void Application : : UninitializeBase ( )
2014-12-04 21:45:15 +01:00
{
Timer : : Uninitialize ( ) ;
GetTP ( ) . Stop ( ) ;
2014-05-03 20:02:22 +02:00
}
2012-05-21 12:53:38 +02:00
/**
* Retrieves a pointer to the application singleton object .
*
* @ returns The application object .
*/
2018-01-04 04:25:35 +01:00
Application : : Ptr Application : : GetInstance ( )
2012-05-21 12:53:38 +02:00
{
2014-11-08 21:17:16 +01:00
return m_Instance ;
2012-05-21 12:53:38 +02:00
}
2018-01-04 04:25:35 +01:00
void Application : : SetResourceLimits ( )
2013-12-05 11:04:47 +01:00
{
2015-01-09 11:43:57 +01:00
# ifdef __linux__
2013-12-05 11:04:47 +01:00
rlimit rl ;
2014-04-22 19:47:19 +02:00
# ifdef RLIMIT_NOFILE
2018-08-09 15:37:23 +02:00
rlim_t fileLimit = Configuration : : RLimitFiles ;
2013-12-05 11:04:47 +01:00
2017-06-21 11:36:14 +02:00
if ( fileLimit ! = 0 ) {
2019-03-08 14:07:29 +01:00
if ( fileLimit < ( rlim_t ) GetDefaultRLimitFiles ( ) ) {
2017-06-21 11:36:14 +02:00
Log ( LogWarning , " Application " )
2017-12-19 15:50:05 +01:00
< < " The user-specified value for RLimitFiles cannot be smaller than the default value ( " < < GetDefaultRLimitFiles ( ) < < " ). Using the default value instead. " ;
2017-06-21 11:36:14 +02:00
fileLimit = GetDefaultRLimitFiles ( ) ;
}
rl . rlim_cur = fileLimit ;
rl . rlim_max = rl . rlim_cur ;
if ( setrlimit ( RLIMIT_NOFILE , & rl ) < 0 )
2018-04-16 13:39:20 +02:00
Log ( LogWarning , " Application " )
2018-04-23 10:18:31 +02:00
< < " Failed to adjust resource limit for open file handles (RLIMIT_NOFILE) with error \" " < < strerror ( errno ) < < " \" " ;
2013-12-05 11:04:47 +01:00
# else /* RLIMIT_NOFILE */
2017-06-21 11:36:14 +02:00
Log ( LogNotice , " Application " , " System does not support adjusting the resource limit for open file handles (RLIMIT_NOFILE) " ) ;
2013-12-05 11:04:47 +01:00
# endif /* RLIMIT_NOFILE */
2017-06-21 11:36:14 +02:00
}
2013-12-05 11:04:47 +01:00
# ifdef RLIMIT_NPROC
2018-08-09 15:37:23 +02:00
rlim_t processLimit = Configuration : : RLimitProcesses ;
2017-06-21 11:36:14 +02:00
if ( processLimit ! = 0 ) {
2019-03-08 14:07:29 +01:00
if ( processLimit < ( rlim_t ) GetDefaultRLimitProcesses ( ) ) {
2017-06-21 11:36:14 +02:00
Log ( LogWarning , " Application " )
2017-12-19 15:50:05 +01:00
< < " The user-specified value for RLimitProcesses cannot be smaller than the default value ( " < < GetDefaultRLimitProcesses ( ) < < " ). Using the default value instead. " ;
2017-06-21 11:36:14 +02:00
processLimit = GetDefaultRLimitProcesses ( ) ;
}
2013-12-05 11:04:47 +01:00
2017-06-21 11:36:14 +02:00
rl . rlim_cur = processLimit ;
rl . rlim_max = rl . rlim_cur ;
if ( setrlimit ( RLIMIT_NPROC , & rl ) < 0 )
2018-04-16 13:39:20 +02:00
Log ( LogWarning , " Application " )
< < " Failed adjust resource limit for number of processes (RLIMIT_NPROC) with error \" " < < strerror ( errno ) < < " \" " ;
2013-12-05 11:04:47 +01:00
# else /* RLIMIT_NPROC */
2017-06-21 11:36:14 +02:00
Log ( LogNotice , " Application " , " System does not support adjusting the resource limit for number of processes (RLIMIT_NPROC) " ) ;
2013-12-05 11:04:47 +01:00
# endif /* RLIMIT_NPROC */
2017-06-21 11:36:14 +02:00
}
2014-04-22 19:47:19 +02:00
# ifdef RLIMIT_STACK
int argc = Application : : GetArgC ( ) ;
char * * argv = Application : : GetArgV ( ) ;
bool set_stack_rlimit = true ;
for ( int i = 0 ; i < argc ; i + + ) {
if ( strcmp ( argv [ i ] , " --no-stack-rlimit " ) = = 0 ) {
set_stack_rlimit = false ;
break ;
}
}
2014-07-21 13:33:01 +02:00
if ( getrlimit ( RLIMIT_STACK , & rl ) < 0 ) {
Log ( LogWarning , " Application " , " Could not determine resource limit for stack size (RLIMIT_STACK) " ) ;
rl . rlim_max = RLIM_INFINITY ;
}
2014-04-22 19:47:19 +02:00
2017-06-21 11:36:14 +02:00
rlim_t stackLimit ;
2014-04-22 19:47:19 +02:00
2018-08-09 15:37:23 +02:00
stackLimit = Configuration : : RLimitStack ;
2014-04-22 19:47:19 +02:00
2017-06-21 11:36:14 +02:00
if ( stackLimit ! = 0 ) {
2019-03-08 14:07:29 +01:00
if ( stackLimit < ( rlim_t ) GetDefaultRLimitStack ( ) ) {
2017-06-21 11:36:14 +02:00
Log ( LogWarning , " Application " )
2017-12-19 15:50:05 +01:00
< < " The user-specified value for RLimitStack cannot be smaller than the default value ( " < < GetDefaultRLimitStack ( ) < < " ). Using the default value instead. " ;
2017-06-21 11:36:14 +02:00
stackLimit = GetDefaultRLimitStack ( ) ;
2014-07-21 13:33:01 +02:00
}
2014-04-22 19:47:19 +02:00
2017-06-21 11:36:14 +02:00
if ( set_stack_rlimit )
rl . rlim_cur = stackLimit ;
else
rl . rlim_cur = rl . rlim_max ;
2014-04-22 19:47:19 +02:00
2018-04-25 14:26:40 +02:00
if ( setrlimit ( RLIMIT_STACK , & rl ) < 0 )
2018-04-16 13:39:20 +02:00
Log ( LogWarning , " Application " )
< < " Failed adjust resource limit for stack size (RLIMIT_STACK) with error \" " < < strerror ( errno ) < < " \" " ;
2018-04-25 14:26:40 +02:00
else if ( set_stack_rlimit ) {
char * * new_argv = static_cast < char * * > ( malloc ( sizeof ( char * ) * ( argc + 2 ) ) ) ;
2014-07-21 13:33:01 +02:00
2018-04-25 14:26:40 +02:00
if ( ! new_argv ) {
perror ( " malloc " ) ;
Exit ( EXIT_FAILURE ) ;
}
2014-08-06 10:35:27 +02:00
2018-04-25 14:26:40 +02:00
new_argv [ 0 ] = argv [ 0 ] ;
new_argv [ 1 ] = strdup ( " --no-stack-rlimit " ) ;
2014-07-21 13:33:01 +02:00
2018-04-25 14:26:40 +02:00
if ( ! new_argv [ 1 ] ) {
perror ( " strdup " ) ;
exit ( 1 ) ;
}
2017-06-21 11:36:14 +02:00
2018-04-25 14:26:40 +02:00
for ( int i = 1 ; i < argc ; i + + )
new_argv [ i + 1 ] = argv [ i ] ;
2017-06-21 11:36:14 +02:00
2018-04-25 14:26:40 +02:00
new_argv [ argc + 1 ] = nullptr ;
2017-06-21 11:36:14 +02:00
2018-04-25 14:26:40 +02:00
( void ) execvp ( new_argv [ 0 ] , new_argv ) ;
perror ( " execvp " ) ;
_exit ( EXIT_FAILURE ) ;
2017-06-21 11:36:14 +02:00
}
2014-04-22 19:47:19 +02:00
# else /* RLIMIT_STACK */
2017-06-21 11:36:14 +02:00
Log ( LogNotice , " Application " , " System does not support adjusting the resource limit for stack size (RLIMIT_STACK) " ) ;
2014-04-22 19:47:19 +02:00
# endif /* RLIMIT_STACK */
2017-06-21 11:36:14 +02:00
}
2015-01-09 11:43:57 +01:00
# endif /* __linux__ */
2013-12-05 11:04:47 +01:00
}
2018-01-04 04:25:35 +01:00
int Application : : GetArgC ( )
2013-02-14 16:18:58 +01:00
{
return m_ArgC ;
}
void Application : : SetArgC ( int argc )
{
m_ArgC = argc ;
}
2018-01-04 04:25:35 +01:00
char * * Application : : GetArgV ( )
2013-02-14 16:18:58 +01:00
{
return m_ArgV ;
}
void Application : : SetArgV ( char * * argv )
{
m_ArgV = argv ;
}
2012-04-24 14:02:15 +02:00
/**
2012-05-18 22:53:35 +02:00
* Processes events for registered sockets and timers and calls whatever
* handlers have been set up for these events .
2012-04-24 14:02:15 +02:00
*/
2018-01-04 04:25:35 +01:00
void Application : : RunEventLoop ( )
2012-03-28 13:24:49 +02:00
{
2014-04-22 19:47:19 +02:00
double lastLoop = Utility : : GetTime ( ) ;
2019-03-08 14:59:01 +01:00
while ( ! m_ShuttingDown ) {
if ( m_RequestRestart ) {
m_RequestRestart = false ; // we are now handling the request, once is enough
2014-04-22 19:47:19 +02:00
2019-07-15 13:59:17 +02:00
# ifdef _WIN32
2019-03-08 14:59:01 +01:00
// are we already restarting? ignore request if we already are
if ( ! l_Restarting ) {
l_Restarting = true ;
m_ReloadProcess = StartReloadProcess ( ) ;
}
2019-07-15 13:59:17 +02:00
# else /* _WIN32 */
2019-07-16 11:43:47 +02:00
Log ( LogNotice , " Application " )
< < " Got reload command, forwarding to umbrella process (PID " < < m_UmbrellaProcess < < " ) " ;
2019-07-15 18:29:15 +02:00
2019-07-15 13:59:17 +02:00
( void ) kill ( m_UmbrellaProcess , SIGHUP ) ;
# endif /* _WIN32 */
2019-03-08 14:59:01 +01:00
} else {
/* Watches for changes to the system time. Adjusts timers if necessary. */
Utility : : Sleep ( 2.5 ) ;
if ( m_RequestReopenLogs ) {
Log ( LogNotice , " Application " , " Reopening log files " ) ;
m_RequestReopenLogs = false ;
OnReopenLogs ( ) ;
}
2014-08-04 14:03:37 +02:00
2019-03-08 14:59:01 +01:00
double now = Utility : : GetTime ( ) ;
double timeDiff = lastLoop - now ;
2014-04-27 21:47:25 +02:00
2019-03-08 14:59:01 +01:00
if ( std : : fabs ( timeDiff ) > 15 ) {
/* We made a significant jump in time. */
Log ( LogInformation , " Application " )
< < " We jumped "
< < ( timeDiff < 0 ? " forward " : " backward " )
< < " in time: " < < std : : fabs ( timeDiff ) < < " seconds " ;
2014-04-28 23:30:56 +02:00
2019-03-08 14:59:01 +01:00
Timer : : AdjustTimers ( - timeDiff ) ;
}
2014-04-28 23:30:56 +02:00
2019-03-08 14:59:01 +01:00
lastLoop = now ;
}
2014-04-27 21:47:25 +02:00
}
2014-08-04 14:03:37 +02:00
2015-03-12 15:53:56 +01:00
Log ( LogInformation , " Application " , " Shutting down... " ) ;
2015-08-15 20:28:05 +02:00
ConfigObject : : StopObjects ( ) ;
2013-10-17 10:56:42 +02:00
Application : : GetInstance ( ) - > OnShutdown ( ) ;
2014-12-04 21:45:15 +01:00
UninitializeBase ( ) ;
2012-09-25 15:24:14 +02:00
}
2018-01-04 04:25:35 +01:00
bool Application : : IsShuttingDown ( )
2016-08-17 19:24:22 +02:00
{
return m_ShuttingDown ;
}
2019-05-03 12:44:29 +02:00
bool Application : : IsRestarting ( )
{
return l_Restarting ;
}
2018-01-04 04:25:35 +01:00
void Application : : OnShutdown ( )
2013-11-04 15:57:46 +01:00
{
/* Nothing to do here. */
}
2015-08-10 15:28:10 +02:00
static void ReloadProcessCallbackInternal ( const ProcessResult & pr )
2014-05-11 17:14:35 +02:00
{
2016-05-11 16:07:28 +02:00
if ( pr . ExitStatus ! = 0 ) {
Application : : SetLastReloadFailed ( Utility : : GetTime ( ) ) ;
2014-05-28 13:17:02 +02:00
Log ( LogCritical , " Application " , " Found error in config: reloading aborted " ) ;
2016-05-11 16:07:28 +02:00
}
2015-08-10 15:28:10 +02:00
# ifdef _WIN32
else
Application : : Exit ( 7 ) ; /* keep this exit code in sync with icinga-app */
# endif /* _WIN32 */
}
static void ReloadProcessCallback ( const ProcessResult & pr )
{
2014-05-11 17:14:35 +02:00
l_Restarting = false ;
2015-08-10 15:28:10 +02:00
2017-11-21 12:12:58 +01:00
std : : thread t ( std : : bind ( & ReloadProcessCallbackInternal , pr ) ) ;
2015-08-10 15:28:10 +02:00
t . detach ( ) ;
2014-05-11 17:14:35 +02:00
}
2018-01-04 04:25:35 +01:00
pid_t Application : : StartReloadProcess ( )
2014-04-27 21:47:25 +02:00
{
// prepare arguments
2018-01-11 11:17:38 +01:00
ArrayData args ;
args . push_back ( GetExePath ( m_ArgV [ 0 ] ) ) ;
2014-04-27 21:47:25 +02:00
2014-04-29 00:05:39 +02:00
for ( int i = 1 ; i < Application : : GetArgC ( ) ; i + + ) {
2014-04-27 21:47:25 +02:00
if ( std : : string ( Application : : GetArgV ( ) [ i ] ) ! = " --reload-internal " )
2018-01-11 11:17:38 +01:00
args . push_back ( Application : : GetArgV ( ) [ i ] ) ;
2014-04-29 00:05:39 +02:00
else
i + + ; // the next parameter after --reload-internal is the pid, remove that too
}
2015-08-10 15:28:10 +02:00
# ifndef _WIN32
2018-01-11 11:17:38 +01:00
args . push_back ( " --reload-internal " ) ;
args . push_back ( Convert : : ToString ( Utility : : GetPid ( ) ) ) ;
2015-08-10 15:28:10 +02:00
# else /* _WIN32 */
2018-06-12 12:19:16 +02:00
args . push_back ( " --validate " ) ;
2015-08-10 15:28:10 +02:00
# endif /* _WIN32 */
2014-04-27 21:47:25 +02:00
2019-04-15 16:56:30 +02:00
double reloadTimeout = Application : : GetReloadTimeout ( ) ;
2018-01-11 11:17:38 +01:00
Process : : Ptr process = new Process ( Process : : PrepareCommand ( new Array ( std : : move ( args ) ) ) ) ;
2019-04-15 16:56:30 +02:00
process - > SetTimeout ( reloadTimeout ) ;
2014-05-11 17:14:35 +02:00
process - > Run ( & ReloadProcessCallback ) ;
2014-08-04 14:03:37 +02:00
2019-02-22 15:53:38 +01:00
Log ( LogInformation , " Application " )
< < " Got reload command: Started new instance with PID ' "
< < ( unsigned long ) ( process - > GetPID ( ) ) < < " ' (timeout is "
2019-04-15 16:56:30 +02:00
< < reloadTimeout < < " s). " ;
2019-02-22 15:53:38 +01:00
2014-05-18 18:04:34 +02:00
return process - > GetPID ( ) ;
2014-04-27 21:47:25 +02:00
}
2012-04-24 14:02:15 +02:00
/**
* Signals the application to shut down during the next
* execution of the event loop .
*/
2018-01-04 04:25:35 +01:00
void Application : : RequestShutdown ( )
2012-03-28 13:24:49 +02:00
{
2015-03-12 11:43:04 +01:00
Log ( LogInformation , " Application " , " Received request to shut down. " ) ;
2012-03-28 13:24:49 +02:00
m_ShuttingDown = true ;
}
2012-03-31 15:18:09 +02:00
2013-08-30 14:27:24 +02:00
/**
* Signals the application to restart during the next
* execution of the event loop .
*/
2018-01-04 04:25:35 +01:00
void Application : : RequestRestart ( )
2013-08-30 14:27:24 +02:00
{
2014-04-27 21:47:25 +02:00
m_RequestRestart = true ;
2013-08-30 14:27:24 +02:00
}
2014-05-22 11:22:30 +02:00
/**
* Signals the application to reopen log files during the
* next execution of the event loop .
*/
2018-01-04 04:25:35 +01:00
void Application : : RequestReopenLogs ( )
2014-05-22 11:22:30 +02:00
{
m_RequestReopenLogs = true ;
}
2019-07-15 13:59:17 +02:00
# ifndef _WIN32
2019-07-16 11:11:10 +02:00
/**
* Sets the PID of the Icinga umbrella process .
*
* @ param pid The PID of the Icinga umbrella process .
*/
2019-07-15 13:59:17 +02:00
void Application : : SetUmbrellaProcess ( pid_t pid )
{
m_UmbrellaProcess = pid ;
}
# endif /* _WIN32 */
2012-04-24 14:02:15 +02:00
/**
2012-07-10 13:31:17 +02:00
* Retrieves the full path of the executable .
2012-04-24 14:02:15 +02:00
*
2012-08-14 12:51:51 +02:00
* @ param argv0 The first command - line argument .
2012-07-10 13:31:17 +02:00
* @ returns The path .
2012-04-24 14:02:15 +02:00
*/
2012-08-14 12:51:51 +02:00
String Application : : GetExePath ( const String & argv0 )
2012-04-02 13:09:33 +02:00
{
2012-08-02 09:38:08 +02:00
String executablePath ;
2012-07-08 21:18:35 +02:00
2012-04-02 13:09:33 +02:00
# ifndef _WIN32
2012-06-15 22:26:25 +02:00
char buffer [ MAXPATHLEN ] ;
2017-12-14 15:37:20 +01:00
if ( ! getcwd ( buffer , sizeof ( buffer ) ) ) {
2013-03-11 13:45:08 +01:00
BOOST_THROW_EXCEPTION ( posix_error ( )
2017-12-19 15:50:05 +01:00
< < boost : : errinfo_api_function ( " getcwd " )
< < boost : : errinfo_errno ( errno ) ) ;
2013-03-11 13:45:08 +01:00
}
2012-08-02 09:38:08 +02:00
String workingDirectory = buffer ;
2012-04-02 13:09:33 +02:00
if ( argv0 [ 0 ] ! = ' / ' )
2012-07-08 21:18:35 +02:00
executablePath = workingDirectory + " / " + argv0 ;
2012-04-02 13:09:33 +02:00
else
2012-07-08 21:18:35 +02:00
executablePath = argv0 ;
2012-04-02 13:09:33 +02:00
2012-08-02 09:38:08 +02:00
bool foundSlash = false ;
2012-08-04 09:58:31 +02:00
for ( size_t i = 0 ; i < argv0 . GetLength ( ) ; i + + ) {
2012-08-02 09:38:08 +02:00
if ( argv0 [ i ] = = ' / ' ) {
foundSlash = true ;
break ;
}
}
if ( ! foundSlash ) {
2018-11-19 14:59:20 +01:00
String pathEnv = Utility : : GetFromEnvironment ( " PATH " ) ;
if ( ! pathEnv . IsEmpty ( ) ) {
2018-01-04 18:24:45 +01:00
std : : vector < String > paths = String ( pathEnv ) . Split ( " : " ) ;
2012-04-02 13:09:33 +02:00
2012-06-15 22:26:25 +02:00
bool foundPath = false ;
2016-08-25 06:19:44 +02:00
for ( const String & path : paths ) {
2012-08-02 09:38:08 +02:00
String pathTest = path + " / " + argv0 ;
2012-04-02 13:09:33 +02:00
2012-08-02 09:38:08 +02:00
if ( access ( pathTest . CStr ( ) , X_OK ) = = 0 ) {
2012-07-08 21:18:35 +02:00
executablePath = pathTest ;
2012-06-15 22:26:25 +02:00
foundPath = true ;
2012-04-02 13:09:33 +02:00
break ;
}
}
2012-06-15 22:26:25 +02:00
if ( ! foundPath ) {
2012-08-02 09:38:08 +02:00
executablePath . Clear ( ) ;
2013-03-16 21:18:53 +01:00
BOOST_THROW_EXCEPTION ( std : : runtime_error ( " Could not determine executable path. " ) ) ;
2012-06-15 22:26:25 +02:00
}
2012-04-02 13:09:33 +02:00
}
}
2017-12-14 15:37:20 +01:00
if ( ! realpath ( executablePath . CStr ( ) , buffer ) ) {
2013-03-11 13:45:08 +01:00
BOOST_THROW_EXCEPTION ( posix_error ( )
2017-12-19 15:50:05 +01:00
< < boost : : errinfo_api_function ( " realpath " )
< < boost : : errinfo_errno ( errno )
< < boost : : errinfo_file_name ( executablePath ) ) ;
2013-03-11 13:45:08 +01:00
}
2012-04-02 13:09:33 +02:00
2012-08-14 12:51:51 +02:00
return buffer ;
2012-04-02 13:09:33 +02:00
# else /* _WIN32 */
char FullExePath [ MAXPATHLEN ] ;
2017-12-14 15:37:20 +01:00
if ( ! GetModuleFileName ( nullptr , FullExePath , sizeof ( FullExePath ) ) )
2013-03-11 14:03:01 +01:00
BOOST_THROW_EXCEPTION ( win32_error ( )
2017-12-19 15:50:05 +01:00
< < boost : : errinfo_api_function ( " GetModuleFileName " )
< < errinfo_win32_error ( GetLastError ( ) ) ) ;
2012-04-02 13:09:33 +02:00
2012-08-14 12:51:51 +02:00
return FullExePath ;
2012-04-02 13:09:33 +02:00
# endif /* _WIN32 */
}
2013-11-15 09:04:39 +01:00
/**
2014-07-23 09:30:56 +02:00
* Display version and path information .
2013-11-15 09:04:39 +01:00
*/
2014-12-20 15:29:04 +01:00
void Application : : DisplayInfoMessage ( std : : ostream & os , bool skipVersion )
2013-11-15 09:04:39 +01:00
{
2018-08-07 18:44:08 +02:00
/* icinga-app prints its own version information, stack traces need it here. */
2014-07-23 09:30:56 +02:00
if ( ! skipVersion )
2018-08-07 18:44:08 +02:00
os < < " Application version: " < < GetAppVersion ( ) < < " \n \n " ;
os < < " System information: \n "
< < " Platform: " < < Utility : : GetPlatformName ( ) < < " \n "
< < " Platform version: " < < Utility : : GetPlatformVersion ( ) < < " \n "
< < " Kernel: " < < Utility : : GetPlatformKernel ( ) < < " \n "
< < " Kernel version: " < < Utility : : GetPlatformKernelVersion ( ) < < " \n "
< < " Architecture: " < < Utility : : GetPlatformArchitecture ( ) < < " \n " ;
2018-08-09 15:37:23 +02:00
Namespace : : Ptr systemNS = ScriptGlobal : : Get ( " System " ) ;
2018-08-07 18:44:08 +02:00
os < < " \n Build information: \n "
2018-08-09 15:37:23 +02:00
< < " Compiler: " < < systemNS - > Get ( " BuildCompilerName " ) < < " " < < systemNS - > Get ( " BuildCompilerVersion " ) < < " \n "
2020-02-13 15:25:03 +01:00
< < " Build host: " < < systemNS - > Get ( " BuildHostName " ) < < " \n "
< < " OpenSSL version: " < < GetOpenSSLVersion ( ) < < " \n " ;
2014-12-20 15:29:04 +01:00
2018-08-07 18:44:08 +02:00
os < < " \n Application information: \n "
< < " \n General paths: \n "
2018-08-09 15:37:23 +02:00
< < " Config directory: " < < Configuration : : ConfigDir < < " \n "
< < " Data directory: " < < Configuration : : DataDir < < " \n "
< < " Log directory: " < < Configuration : : LogDir < < " \n "
< < " Cache directory: " < < Configuration : : CacheDir < < " \n "
< < " Spool directory: " < < Configuration : : SpoolDir < < " \n "
< < " Run directory: " < < Configuration : : InitRunDir < < " \n "
2018-08-07 18:44:08 +02:00
< < " \n Old paths (deprecated): \n "
2018-08-09 15:37:23 +02:00
< < " Installation root: " < < Configuration : : PrefixDir < < " \n "
< < " Sysconf directory: " < < Configuration : : SysconfDir < < " \n "
< < " Run directory (base): " < < Configuration : : RunDir < < " \n "
< < " Local state directory: " < < Configuration : : LocalStateDir < < " \n "
2018-08-07 18:44:08 +02:00
< < " \n Internal paths: \n "
2018-08-09 15:37:23 +02:00
< < " Package data directory: " < < Configuration : : PkgDataDir < < " \n "
< < " State path: " < < Configuration : : StatePath < < " \n "
< < " Modified attributes path: " < < Configuration : : ModAttrPath < < " \n "
< < " Objects path: " < < Configuration : : ObjectsPath < < " \n "
< < " Vars path: " < < Configuration : : VarsPath < < " \n "
< < " PID path: " < < Configuration : : PidPath < < " \n " ;
2015-03-16 13:32:13 +01:00
2013-11-15 09:04:39 +01:00
}
2013-02-01 19:36:47 +01:00
/**
* Displays a message that tells users what to do when they encounter a bug .
*/
2014-12-20 15:29:04 +01:00
void Application : : DisplayBugMessage ( std : : ostream & os )
2013-02-01 19:36:47 +01:00
{
2014-12-20 15:29:04 +01:00
os < < " *** " < < " \n "
2017-12-19 15:50:05 +01:00
< < " * This would indicate a runtime problem or configuration error. If you believe this is a bug in Icinga 2 " < < " \n "
< < " * please submit a bug report at https://github.com/Icinga/icinga2 and include this stack trace as well as any other " < < " \n "
< < " * information that might be useful in order to reproduce this problem. " < < " \n "
< < " *** " < < " \n " ;
2013-02-01 19:36:47 +01:00
}
2018-01-04 04:25:35 +01:00
String Application : : GetCrashReportFilename ( )
2014-12-22 14:14:16 +01:00
{
2018-08-09 15:37:23 +02:00
return Configuration : : LogDir + " /crash/report. " + Convert : : ToString ( Utility : : GetTime ( ) ) ;
2014-12-22 14:14:16 +01:00
}
2015-03-03 13:53:11 +01:00
2015-08-08 09:40:16 +02:00
void Application : : AttachDebugger ( const String & filename , bool interactive )
2015-03-03 13:53:11 +01:00
{
2015-08-08 09:40:16 +02:00
# ifndef _WIN32
2015-03-03 13:53:11 +01:00
# ifdef __linux__
prctl ( PR_SET_DUMPABLE , 1 ) ;
# endif /* __linux __ */
String my_pid = Convert : : ToString ( Utility : : GetPid ( ) ) ;
pid_t pid = fork ( ) ;
if ( pid < 0 ) {
BOOST_THROW_EXCEPTION ( posix_error ( )
2017-12-19 15:50:05 +01:00
< < boost : : errinfo_api_function ( " fork " )
< < boost : : errinfo_errno ( errno ) ) ;
2015-03-03 13:53:11 +01:00
}
if ( pid = = 0 ) {
2015-08-08 09:40:16 +02:00
if ( ! interactive ) {
int fd = open ( filename . CStr ( ) , O_CREAT | O_RDWR | O_APPEND , 0600 ) ;
if ( fd < 0 ) {
BOOST_THROW_EXCEPTION ( posix_error ( )
2017-12-19 15:50:05 +01:00
< < boost : : errinfo_api_function ( " open " )
< < boost : : errinfo_errno ( errno )
< < boost : : errinfo_file_name ( filename ) ) ;
2015-08-08 09:40:16 +02:00
}
2015-03-03 13:53:11 +01:00
2015-08-08 09:40:16 +02:00
if ( fd ! = 1 ) {
/* redirect stdout to the file */
dup2 ( fd , 1 ) ;
close ( fd ) ;
}
2015-03-03 13:53:11 +01:00
2015-08-08 09:40:16 +02:00
/* redirect stderr to stdout */
if ( fd ! = 2 )
close ( 2 ) ;
dup2 ( 1 , 2 ) ;
2015-03-03 13:53:11 +01:00
}
2015-08-08 09:40:16 +02:00
char * * argv ;
char * my_pid_str = strdup ( my_pid . CStr ( ) ) ;
2015-03-03 13:53:11 +01:00
2015-08-08 09:40:16 +02:00
if ( interactive ) {
const char * uargv [ ] = {
" gdb " ,
" -p " ,
my_pid_str ,
2017-12-14 15:37:20 +01:00
nullptr
2015-08-08 09:40:16 +02:00
} ;
2017-12-14 15:04:25 +01:00
2015-08-08 09:40:16 +02:00
argv = const_cast < char * * > ( uargv ) ;
2017-12-14 15:04:25 +01:00
( void ) execvp ( argv [ 0 ] , argv ) ;
2015-08-08 09:40:16 +02:00
} else {
const char * uargv [ ] = {
" gdb " ,
" --batch " ,
" -p " ,
my_pid_str ,
" -ex " ,
" thread apply all bt full " ,
" -ex " ,
" detach " ,
" -ex " ,
" quit " ,
2017-12-14 15:37:20 +01:00
nullptr
2015-08-08 09:40:16 +02:00
} ;
2017-12-14 15:04:25 +01:00
2015-08-08 09:40:16 +02:00
argv = const_cast < char * * > ( uargv ) ;
2017-12-14 15:04:25 +01:00
( void ) execvp ( argv [ 0 ] , argv ) ;
2015-08-08 09:40:16 +02:00
}
2015-03-03 13:53:11 +01:00
perror ( " Failed to launch GDB " ) ;
free ( my_pid_str ) ;
_exit ( 0 ) ;
}
int status ;
if ( waitpid ( pid , & status , 0 ) < 0 ) {
BOOST_THROW_EXCEPTION ( posix_error ( )
2017-12-19 15:50:05 +01:00
< < boost : : errinfo_api_function ( " waitpid " )
< < boost : : errinfo_errno ( errno ) ) ;
2015-03-03 13:53:11 +01:00
}
# ifdef __linux__
prctl ( PR_SET_DUMPABLE , 0 ) ;
# endif /* __linux __ */
2015-08-08 09:40:16 +02:00
# else /* _WIN32 */
DebugBreak ( ) ;
# endif /* _WIN32 */
2015-03-03 13:53:11 +01:00
}
2014-05-22 11:22:30 +02:00
/**
* Signal handler for SIGUSR1 . This signal causes Icinga to re - open
* its log files and is mainly for use by logrotate .
*
* @ param - The signal number .
*/
void Application : : SigUsr1Handler ( int )
{
2019-04-24 11:33:43 +02:00
Log ( LogInformation , " Application " )
< < " Received USR1 signal, reopening application logs. " ;
2014-05-22 11:22:30 +02:00
RequestReopenLogs ( ) ;
}
2013-01-30 11:51:15 +01:00
/**
2013-03-07 16:00:10 +01:00
* Signal handler for SIGABRT . Helps with debugging ASSERT ( ) s .
2013-01-30 11:51:15 +01:00
*
2013-08-20 11:06:04 +02:00
* @ param - The signal number .
2013-01-30 11:51:15 +01:00
*/
2013-08-20 11:06:04 +02:00
void Application : : SigAbrtHandler ( int )
2013-01-30 11:51:15 +01:00
{
2013-03-07 15:00:26 +01:00
# ifndef _WIN32
2013-03-06 11:03:50 +01:00
struct sigaction sa ;
memset ( & sa , 0 , sizeof ( sa ) ) ;
sa . sa_handler = SIG_DFL ;
2017-12-14 15:37:20 +01:00
sigaction ( SIGABRT , & sa , nullptr ) ;
2013-03-07 15:00:26 +01:00
# endif /* _WIN32 */
2013-03-06 11:03:50 +01:00
2013-11-15 09:04:39 +01:00
std : : cerr < < " Caught SIGABRT. " < < std : : endl
2017-12-19 15:50:05 +01:00
< < " Current time: " < < Utility : : FormatDateTime ( " %Y-%m-%d %H:%M:%S %z " , Utility : : GetTime ( ) ) < < std : : endl
< < std : : endl ;
2013-11-15 09:04:39 +01:00
2014-12-20 15:29:04 +01:00
String fname = GetCrashReportFilename ( ) ;
2015-12-08 15:12:13 +01:00
String dirName = Utility : : DirName ( fname ) ;
if ( ! Utility : : PathExists ( dirName ) ) {
# ifndef _WIN32
if ( mkdir ( dirName . CStr ( ) , 0700 ) < 0 & & errno ! = EEXIST ) {
# else /*_ WIN32 */
if ( mkdir ( dirName . CStr ( ) ) < 0 & & errno ! = EEXIST ) {
# endif /* _WIN32 */
std : : cerr < < " Could not create directory ' " < < dirName < < " ': Error " < < errno < < " , " < < strerror ( errno ) < < " \n " ;
}
}
2015-03-11 16:06:04 +01:00
2018-08-09 15:37:23 +02:00
bool interactive_debugger = Configuration : : AttachDebugger ;
2014-12-20 15:29:04 +01:00
2015-08-08 09:40:16 +02:00
if ( ! interactive_debugger ) {
std : : ofstream ofs ;
ofs . open ( fname . CStr ( ) ) ;
2014-12-20 15:29:04 +01:00
2015-08-08 09:40:16 +02:00
Log ( LogCritical , " Application " )
2017-12-19 15:50:05 +01:00
< < " Icinga 2 has terminated unexpectedly. Additional information can be found in ' " < < fname < < " ' " < < " \n " ;
2013-01-30 11:51:15 +01:00
2015-08-08 09:40:16 +02:00
DisplayInfoMessage ( ofs ) ;
2014-12-20 15:29:04 +01:00
2015-08-08 09:40:16 +02:00
StackTrace trace ;
ofs < < " Stacktrace: " < < " \n " ;
trace . Print ( ofs , 1 ) ;
2013-02-01 14:46:06 +01:00
2015-08-08 09:40:16 +02:00
DisplayBugMessage ( ofs ) ;
2015-03-03 13:53:11 +01:00
2015-08-08 09:40:16 +02:00
ofs < < " \n " ;
ofs . close ( ) ;
} else {
2015-12-08 15:12:13 +01:00
Log ( LogCritical , " Application " , " Icinga 2 has terminated unexpectedly. Attaching debugger... " ) ;
2015-08-08 09:40:16 +02:00
}
AttachDebugger ( fname , interactive_debugger ) ;
2013-01-30 11:51:15 +01:00
}
2015-08-08 09:40:16 +02:00
# ifdef _WIN32
2012-05-21 12:53:38 +02:00
/**
* Console control handler . Prepares the application for cleanly
* shutting down during the next execution of the event loop .
*/
BOOL WINAPI Application : : CtrlHandler ( DWORD type )
{
2013-03-02 09:07:47 +01:00
Application : : Ptr instance = Application : : GetInstance ( ) ;
2012-06-20 15:23:10 +02:00
if ( ! instance )
return TRUE ;
2013-03-01 12:07:52 +01:00
instance - > RequestShutdown ( ) ;
2012-06-20 15:23:10 +02:00
2017-12-14 15:37:20 +01:00
SetConsoleCtrlHandler ( nullptr , FALSE ) ;
2012-05-21 12:53:38 +02:00
return TRUE ;
}
2017-08-22 10:26:23 +02:00
2018-01-04 04:25:35 +01:00
bool Application : : IsProcessElevated ( ) {
2017-08-22 10:26:23 +02:00
BOOL fIsElevated = FALSE ;
DWORD dwError = ERROR_SUCCESS ;
2017-12-14 15:37:20 +01:00
HANDLE hToken = nullptr ;
2017-08-22 10:26:23 +02:00
if ( ! OpenProcessToken ( GetCurrentProcess ( ) , TOKEN_QUERY , & hToken ) )
dwError = GetLastError ( ) ;
else {
TOKEN_ELEVATION elevation ;
DWORD dwSize ;
if ( ! GetTokenInformation ( hToken , TokenElevation , & elevation , sizeof ( elevation ) , & dwSize ) )
dwError = GetLastError ( ) ;
else
fIsElevated = elevation . TokenIsElevated ;
}
if ( hToken ) {
CloseHandle ( hToken ) ;
2017-12-14 15:37:20 +01:00
hToken = nullptr ;
2017-08-22 10:26:23 +02:00
}
if ( ERROR_SUCCESS ! = dwError ) {
2017-12-14 15:37:20 +01:00
LPSTR mBuf = nullptr ;
2017-08-22 10:26:23 +02:00
if ( ! FormatMessage ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS ,
2017-12-14 15:37:20 +01:00
nullptr , dwError , MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) , mBuf , 0 , nullptr ) )
2017-08-22 10:26:23 +02:00
BOOST_THROW_EXCEPTION ( std : : runtime_error ( " Failed to format error message, last error was: " + dwError ) ) ;
else
BOOST_THROW_EXCEPTION ( std : : runtime_error ( mBuf ) ) ;
}
return fIsElevated ;
}
2012-04-22 16:45:31 +02:00
# endif /* _WIN32 */
2012-04-06 08:56:52 +02:00
2013-01-30 11:51:15 +01:00
/**
* Handler for unhandled exceptions .
*/
2018-01-04 04:25:35 +01:00
void Application : : ExceptionHandler ( )
2013-01-30 11:51:15 +01:00
{
2014-09-09 14:49:21 +02:00
if ( l_InExceptionHandler )
for ( ; ; )
Utility : : Sleep ( 5 ) ;
l_InExceptionHandler = true ;
2013-03-06 11:03:50 +01:00
# ifndef _WIN32
struct sigaction sa ;
memset ( & sa , 0 , sizeof ( sa ) ) ;
sa . sa_handler = SIG_DFL ;
2017-12-14 15:37:20 +01:00
sigaction ( SIGABRT , & sa , nullptr ) ;
2013-03-06 11:03:50 +01:00
# endif /* _WIN32 */
2014-12-20 15:29:04 +01:00
String fname = GetCrashReportFilename ( ) ;
2015-12-08 15:12:13 +01:00
String dirName = Utility : : DirName ( fname ) ;
if ( ! Utility : : PathExists ( dirName ) ) {
# ifndef _WIN32
if ( mkdir ( dirName . CStr ( ) , 0700 ) < 0 & & errno ! = EEXIST ) {
# else /*_ WIN32 */
if ( mkdir ( dirName . CStr ( ) ) < 0 & & errno ! = EEXIST ) {
# endif /* _WIN32 */
std : : cerr < < " Could not create directory ' " < < dirName < < " ': Error " < < errno < < " , " < < strerror ( errno ) < < " \n " ;
}
}
2015-03-11 16:06:04 +01:00
2018-08-09 15:37:23 +02:00
bool interactive_debugger = Configuration : : AttachDebugger ;
2013-11-15 09:04:39 +01:00
2015-12-08 15:12:13 +01:00
if ( ! interactive_debugger ) {
2015-08-08 09:40:16 +02:00
std : : ofstream ofs ;
ofs . open ( fname . CStr ( ) ) ;
2014-12-20 15:29:04 +01:00
2015-08-08 09:40:16 +02:00
ofs < < " Caught unhandled exception. " < < " \n "
2017-12-19 15:50:05 +01:00
< < " Current time: " < < Utility : : FormatDateTime ( " %Y-%m-%d %H:%M:%S %z " , Utility : : GetTime ( ) ) < < " \n "
< < " \n " ;
2013-11-15 09:04:39 +01:00
2015-08-08 09:40:16 +02:00
DisplayInfoMessage ( ofs ) ;
2014-12-20 15:29:04 +01:00
2015-08-08 09:40:16 +02:00
try {
RethrowUncaughtException ( ) ;
} catch ( const std : : exception & ex ) {
Log ( LogCritical , " Application " )
2017-12-19 15:50:05 +01:00
< < DiagnosticInformation ( ex , false ) < < " \n "
< < " \n "
< < " Additional information is available in ' " < < fname < < " ' " < < " \n " ;
2015-08-08 09:40:16 +02:00
ofs < < " \n "
2017-12-19 15:50:05 +01:00
< < DiagnosticInformation ( ex )
< < " \n " ;
2015-08-08 09:40:16 +02:00
}
2013-01-30 11:51:15 +01:00
2015-08-08 09:40:16 +02:00
DisplayBugMessage ( ofs ) ;
2014-12-20 15:29:04 +01:00
2015-08-08 09:40:16 +02:00
ofs . close ( ) ;
}
2013-02-01 19:11:15 +01:00
2015-08-08 09:40:16 +02:00
AttachDebugger ( fname , interactive_debugger ) ;
2015-03-03 13:53:11 +01:00
2013-01-30 11:51:15 +01:00
abort ( ) ;
}
2013-03-07 15:00:26 +01:00
# ifdef _WIN32
LONG CALLBACK Application : : SEHUnhandledExceptionFilter ( PEXCEPTION_POINTERS exi )
{
2014-09-09 14:49:21 +02:00
if ( l_InExceptionHandler )
return EXCEPTION_CONTINUE_SEARCH ;
l_InExceptionHandler = true ;
2014-12-20 15:29:04 +01:00
String fname = GetCrashReportFilename ( ) ;
2015-12-08 15:12:13 +01:00
String dirName = Utility : : DirName ( fname ) ;
if ( ! Utility : : PathExists ( dirName ) ) {
# ifndef _WIN32
if ( mkdir ( dirName . CStr ( ) , 0700 ) < 0 & & errno ! = EEXIST ) {
# else /*_ WIN32 */
if ( mkdir ( dirName . CStr ( ) ) < 0 & & errno ! = EEXIST ) {
# endif /* _WIN32 */
std : : cerr < < " Could not create directory ' " < < dirName < < " ': Error " < < errno < < " , " < < strerror ( errno ) < < " \n " ;
}
}
2015-03-11 16:06:04 +01:00
2014-12-20 15:29:04 +01:00
std : : ofstream ofs ;
ofs . open ( fname . CStr ( ) ) ;
2013-11-15 09:04:39 +01:00
2014-12-20 15:29:04 +01:00
Log ( LogCritical , " Application " )
2017-12-19 15:50:05 +01:00
< < " Icinga 2 has terminated unexpectedly. Additional information can be found in ' " < < fname < < " ' " ;
2014-12-20 15:29:04 +01:00
DisplayInfoMessage ( ofs ) ;
ofs < < " Caught unhandled SEH exception. " < < " \n "
2017-12-19 15:50:05 +01:00
< < " Current time: " < < Utility : : FormatDateTime ( " %Y-%m-%d %H:%M:%S %z " , Utility : : GetTime ( ) ) < < " \n "
< < " \n " ;
2013-03-07 15:00:26 +01:00
StackTrace trace ( exi ) ;
2014-12-20 15:29:04 +01:00
ofs < < " Stacktrace: " < < " \n " ;
trace . Print ( ofs , 1 ) ;
2013-03-07 15:00:26 +01:00
2014-12-20 15:29:04 +01:00
DisplayBugMessage ( ofs ) ;
2013-03-07 15:00:26 +01:00
return EXCEPTION_CONTINUE_SEARCH ;
}
2013-03-09 12:56:40 +01:00
# endif /* _WIN32 */
2013-01-30 11:51:15 +01:00
/**
* Installs the exception handlers .
*/
2018-01-04 04:25:35 +01:00
void Application : : InstallExceptionHandlers ( )
2013-01-30 11:51:15 +01:00
{
std : : set_terminate ( & Application : : ExceptionHandler ) ;
# ifndef _WIN32
struct sigaction sa ;
memset ( & sa , 0 , sizeof ( sa ) ) ;
sa . sa_handler = & Application : : SigAbrtHandler ;
2017-12-14 15:37:20 +01:00
sigaction ( SIGABRT , & sa , nullptr ) ;
2013-03-07 15:00:26 +01:00
# else /* _WIN32 */
SetUnhandledExceptionFilter ( & Application : : SEHUnhandledExceptionFilter ) ;
2013-01-30 11:51:15 +01:00
# endif /* _WIN32 */
}
2012-04-24 14:02:15 +02:00
/**
2012-05-15 10:58:14 +02:00
* Runs the application .
2012-04-24 14:02:15 +02:00
*
* @ returns The application ' s exit code .
*/
2018-01-04 04:25:35 +01:00
int Application : : Run ( )
2012-04-06 08:56:52 +02:00
{
# ifndef _WIN32
struct sigaction sa ;
memset ( & sa , 0 , sizeof ( sa ) ) ;
2014-05-22 11:22:30 +02:00
sa . sa_handler = & Application : : SigUsr1Handler ;
2017-12-14 15:37:20 +01:00
sigaction ( SIGUSR1 , & sa , nullptr ) ;
2013-01-30 11:51:15 +01:00
# else /* _WIN32 */
2012-05-21 12:53:38 +02:00
SetConsoleCtrlHandler ( & Application : : CtrlHandler , TRUE ) ;
2012-04-06 08:56:52 +02:00
# endif /* _WIN32 */
2019-07-15 16:08:08 +02:00
# ifdef _WIN32
2014-06-05 17:44:41 +02:00
try {
2018-08-09 15:37:23 +02:00
UpdatePidFile ( Configuration : : PidPath ) ;
2014-08-25 08:35:35 +02:00
} catch ( const std : : exception & ) {
2014-10-19 17:52:17 +02:00
Log ( LogCritical , " Application " )
2018-08-09 15:37:23 +02:00
< < " Cannot update PID file ' " < < Configuration : : PidPath < < " '. Aborting. " ;
2015-03-02 12:41:02 +01:00
return EXIT_FAILURE ;
2014-06-05 17:44:41 +02:00
}
2019-07-15 16:08:08 +02:00
# endif /* _WIN32 */
2013-09-10 16:03:36 +02:00
2020-02-11 12:49:40 +01:00
SetStartTime ( Utility : : GetTime ( ) ) ;
2016-05-11 12:50:08 +02:00
2015-03-02 12:41:02 +01:00
return Main ( ) ;
2012-04-16 16:27:41 +02:00
}
2012-07-12 17:03:34 +02:00
2018-01-04 18:24:45 +01:00
void Application : : UpdatePidFile ( const String & filename )
{
UpdatePidFile ( filename , Utility : : GetPid ( ) ) ;
}
2012-09-14 14:41:17 +02:00
/**
2012-09-27 09:58:16 +02:00
* Grabs the PID file lock and updates the PID . Terminates the application
* if the PID file is already locked by another instance of the application .
2012-09-14 14:41:17 +02:00
*
* @ param filename The name of the PID file .
2014-05-17 23:07:10 +02:00
* @ param pid The PID to write ; default is the current PID
2012-09-14 14:41:17 +02:00
*/
2014-05-17 23:07:10 +02:00
void Application : : UpdatePidFile ( const String & filename , pid_t pid )
2012-07-12 17:03:34 +02:00
{
2013-03-02 09:07:47 +01:00
ObjectLock olock ( this ) ;
2017-12-14 15:37:20 +01:00
if ( m_PidFile )
2013-03-04 15:52:42 +01:00
fclose ( m_PidFile ) ;
2012-07-12 17:03:34 +02:00
/* There's just no sane way of getting a file descriptor for a
* C + + ofstream which is why we ' re using FILEs here . */
2013-03-12 16:02:35 +01:00
m_PidFile = fopen ( filename . CStr ( ) , " r+ " ) ;
2017-12-14 15:37:20 +01:00
if ( ! m_PidFile )
2013-03-12 16:02:35 +01:00
m_PidFile = fopen ( filename . CStr ( ) , " w " ) ;
2012-07-12 17:03:34 +02:00
2017-12-14 15:37:20 +01:00
if ( ! m_PidFile ) {
2014-10-19 17:52:17 +02:00
Log ( LogCritical , " Application " )
2017-12-19 15:50:05 +01:00
< < " Could not open PID file ' " < < filename < < " '. " ;
2013-03-16 21:18:53 +01:00
BOOST_THROW_EXCEPTION ( std : : runtime_error ( " Could not open PID file ' " + filename + " ' " ) ) ;
2014-06-05 17:44:41 +02:00
}
2012-07-12 17:03:34 +02:00
2013-02-13 12:47:51 +01:00
# ifndef _WIN32
2013-03-02 09:07:47 +01:00
int fd = fileno ( m_PidFile ) ;
Utility : : SetCloExec ( fd ) ;
2013-11-24 00:27:10 +01:00
struct flock lock ;
lock . l_start = 0 ;
lock . l_len = 0 ;
lock . l_type = F_WRLCK ;
lock . l_whence = SEEK_SET ;
if ( fcntl ( fd , F_SETLK , & lock ) < 0 ) {
2014-05-28 13:17:02 +02:00
Log ( LogCritical , " Application " , " Could not lock PID file. Make sure that only one instance of the application is running. " ) ;
2013-03-02 09:07:47 +01:00
2014-08-04 16:43:34 +02:00
Application : : Exit ( EXIT_FAILURE ) ;
2013-03-02 09:07:47 +01:00
}
2013-03-12 14:06:59 +01:00
2013-08-28 10:48:19 +02:00
if ( ftruncate ( fd , 0 ) < 0 ) {
2014-10-19 17:52:17 +02:00
Log ( LogCritical , " Application " )
2017-12-19 15:50:05 +01:00
< < " ftruncate() failed with error code " < < errno < < " , \" " < < Utility : : FormatErrorNumber ( errno ) < < " \" " ;
2014-06-05 17:44:41 +02:00
2013-08-28 10:48:19 +02:00
BOOST_THROW_EXCEPTION ( posix_error ( )
2017-12-19 15:50:05 +01:00
< < boost : : errinfo_api_function ( " ftruncate " )
< < boost : : errinfo_errno ( errno ) ) ;
2013-08-28 10:48:19 +02:00
}
2012-07-13 15:29:39 +02:00
# endif /* _WIN32 */
2012-07-12 17:03:34 +02:00
2015-03-02 12:40:02 +01:00
fprintf ( m_PidFile , " %lu \n " , ( unsigned long ) pid ) ;
2012-07-12 17:03:34 +02:00
fflush ( m_PidFile ) ;
}
2012-09-14 14:41:17 +02:00
/**
* Closes the PID file . Does nothing if the PID file is not currently open .
*/
2014-05-18 16:23:16 +02:00
void Application : : ClosePidFile ( bool unlink )
2012-07-12 17:03:34 +02:00
{
2013-03-02 09:07:47 +01:00
ObjectLock olock ( this ) ;
2017-12-14 15:37:20 +01:00
if ( m_PidFile ) {
2014-05-18 16:23:16 +02:00
if ( unlink ) {
2018-08-09 15:37:23 +02:00
String pidpath = Configuration : : PidPath ;
2014-05-18 16:23:16 +02:00
: : unlink ( pidpath . CStr ( ) ) ;
}
2012-07-12 17:03:34 +02:00
fclose ( m_PidFile ) ;
2014-05-18 16:23:16 +02:00
}
2012-07-12 17:03:34 +02:00
2017-12-14 15:37:20 +01:00
m_PidFile = nullptr ;
2012-07-12 17:03:34 +02:00
}
2014-04-14 00:16:48 +02:00
/**
* Checks if another process currently owns the pidfile and read it
*
* @ param filename The name of the PID file .
2014-05-01 18:42:36 +02:00
* @ returns 0 : no process owning the pidfile , pid of the process otherwise
2014-04-14 00:16:48 +02:00
*/
pid_t Application : : ReadPidFile ( const String & filename )
{
FILE * pidfile = fopen ( filename . CStr ( ) , " r " ) ;
2017-12-14 15:37:20 +01:00
if ( ! pidfile )
2014-05-01 18:42:36 +02:00
return 0 ;
2014-04-14 00:16:48 +02:00
# ifndef _WIN32
int fd = fileno ( pidfile ) ;
struct flock lock ;
lock . l_start = 0 ;
lock . l_len = 0 ;
lock . l_type = F_WRLCK ;
lock . l_whence = SEEK_SET ;
if ( fcntl ( fd , F_GETLK , & lock ) < 0 ) {
int error = errno ;
fclose ( pidfile ) ;
BOOST_THROW_EXCEPTION ( posix_error ( )
2017-12-19 15:50:05 +01:00
< < boost : : errinfo_api_function ( " fcntl " )
< < boost : : errinfo_errno ( error ) ) ;
2014-04-14 00:16:48 +02:00
}
if ( lock . l_type = = F_UNLCK ) {
// nobody has locked the file: no icinga running
fclose ( pidfile ) ;
return - 1 ;
}
# endif /* _WIN32 */
pid_t runningpid ;
int res = fscanf ( pidfile , " %d " , & runningpid ) ;
fclose ( pidfile ) ;
// bogus result?
if ( res ! = 1 )
2014-05-01 18:42:36 +02:00
return 0 ;
2014-04-14 00:16:48 +02:00
# ifdef _WIN32
2014-05-01 18:42:36 +02:00
HANDLE hProcess = OpenProcess ( 0 , FALSE , runningpid ) ;
if ( ! hProcess )
return 0 ;
CloseHandle ( hProcess ) ;
2014-04-14 00:16:48 +02:00
# endif /* _WIN32 */
return runningpid ;
}
2018-01-04 04:25:35 +01:00
int Application : : GetDefaultRLimitFiles ( )
2017-06-21 11:36:14 +02:00
{
return 16 * 1024 ;
}
2018-01-04 04:25:35 +01:00
int Application : : GetDefaultRLimitProcesses ( )
2017-06-21 11:36:14 +02:00
{
return 16 * 1024 ;
}
2018-01-04 04:25:35 +01:00
int Application : : GetDefaultRLimitStack ( )
2017-06-21 11:36:14 +02:00
{
return 256 * 1024 ;
}
2019-04-15 16:56:30 +02:00
double Application : : GetReloadTimeout ( )
2018-01-16 12:58:45 +01:00
{
2019-04-15 16:56:30 +02:00
return ScriptGlobal : : Get ( " ReloadTimeout " ) ;
2018-01-16 12:58:45 +01:00
}
2013-02-15 06:47:26 +01:00
/**
2013-03-25 18:36:15 +01:00
* Returns the global thread pool .
2013-02-15 06:47:26 +01:00
*
2013-03-25 18:36:15 +01:00
* @ returns The global thread pool .
2013-02-15 06:47:26 +01:00
*/
2018-01-04 04:25:35 +01:00
ThreadPool & Application : : GetTP ( )
2013-02-15 06:47:26 +01:00
{
2013-03-25 18:36:15 +01:00
static ThreadPool tp ;
return tp ;
2013-02-15 06:47:26 +01:00
}
2013-10-09 08:46:10 +02:00
2018-01-04 04:25:35 +01:00
double Application : : GetStartTime ( )
2013-10-26 09:41:45 +02:00
{
return m_StartTime ;
}
void Application : : SetStartTime ( double ts )
{
m_StartTime = ts ;
}
2015-09-22 18:18:29 +02:00
2020-01-31 16:49:49 +01:00
double Application : : GetUptime ( )
2016-05-11 12:50:08 +02:00
{
2020-01-31 16:49:49 +01:00
return Utility : : GetTime ( ) - m_StartTime ;
2016-05-11 12:50:08 +02:00
}
2018-01-04 04:25:35 +01:00
bool Application : : GetScriptDebuggerEnabled ( )
2015-11-05 14:29:45 +01:00
{
return m_ScriptDebuggerEnabled ;
}
void Application : : SetScriptDebuggerEnabled ( bool enabled )
{
m_ScriptDebuggerEnabled = enabled ;
}
2018-01-04 04:25:35 +01:00
double Application : : GetLastReloadFailed ( )
2016-05-11 16:07:28 +02:00
{
return m_LastReloadFailed ;
}
void Application : : SetLastReloadFailed ( double ts )
{
m_LastReloadFailed = ts ;
}
2018-01-11 07:08:09 +01:00
void Application : : ValidateName ( const Lazy < String > & lvalue , const ValidationUtils & utils )
2015-09-22 18:18:29 +02:00
{
2018-01-11 07:08:09 +01:00
ObjectImpl < Application > : : ValidateName ( lvalue , utils ) ;
2015-09-22 18:18:29 +02:00
2018-01-11 07:08:09 +01:00
if ( lvalue ( ) ! = " app " )
2017-11-30 18:09:38 +01:00
BOOST_THROW_EXCEPTION ( ValidationError ( this , { " name " } , " Application object must be named 'app'. " ) ) ;
2015-09-22 18:18:29 +02:00
}