diff --git a/base/application.cpp b/base/application.cpp index d5255fea6..c17edeeb5 100644 --- a/base/application.cpp +++ b/base/application.cpp @@ -18,13 +18,14 @@ ******************************************************************************/ #include "i2-base.h" + #ifndef _WIN32 # include #endif /* _WIN32 */ using namespace icinga; -Application::Ptr Application::m_Instance; +Application *Application::m_Instance = NULL; bool Application::m_ShuttingDown = false; bool Application::m_Debugging = false; boost::thread::id Application::m_MainThreadID; @@ -32,9 +33,12 @@ boost::thread::id Application::m_MainThreadID; /** * Constructor for the Application class. */ -Application::Application(void) - : m_PidFile(NULL) +Application::Application(const Dictionary::Ptr& serializedUpdate) + : DynamicObject(serializedUpdate), m_PidFile(NULL) { + if (!IsLocal()) + throw_exception(runtime_error("Application objects must be local.")); + #ifdef _WIN32 /* disable GUI-based error messages for LoadLibrary() */ SetErrorMode(SEM_FAILCRITICALERRORS); @@ -42,8 +46,6 @@ Application::Application(void) WSADATA wsaData; if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) throw_exception(Win32Exception("WSAStartup failed", WSAGetLastError())); -#else /* _WIN32 */ - lt_dlinit(); #endif /* _WIN32 */ char *debugging = getenv("_DEBUG"); @@ -53,6 +55,9 @@ Application::Application(void) if (IsDebuggerPresent()) m_Debugging = true; #endif /* _WIN32 */ + + assert(m_Instance == NULL); + m_Instance = this; } /** @@ -60,6 +65,8 @@ Application::Application(void) */ Application::~Application(void) { + m_Instance = NULL; + m_ShuttingDown = true; DynamicObject::DeactivateObjects(); @@ -78,7 +85,10 @@ Application::~Application(void) */ Application::Ptr Application::GetInstance(void) { - return m_Instance; + if (m_Instance) + return m_Instance->GetSelf(); + else + return Application::Ptr(); } /** @@ -130,20 +140,14 @@ void Application::Shutdown(void) /** * Retrieves the full path of the executable. * + * @param argv0 The first command-line argument. * @returns The path. */ -String Application::GetExePath(void) const +String Application::GetExePath(const String& argv0) { - static String result; - - if (!result.IsEmpty()) - return result; - String executablePath; #ifndef _WIN32 - String argv0 = m_Arguments[0]; - char buffer[MAXPATHLEN]; if (getcwd(buffer, sizeof(buffer)) == NULL) throw_exception(PosixException("getcwd failed", errno)); @@ -189,17 +193,15 @@ String Application::GetExePath(void) const if (realpath(executablePath.CStr(), buffer) == NULL) throw_exception(PosixException("realpath failed", errno)); - result = buffer; + return buffer; #else /* _WIN32 */ char FullExePath[MAXPATHLEN]; if (!GetModuleFileName(NULL, FullExePath, sizeof(FullExePath))) throw_exception(Win32Exception("GetModuleFileName() failed", GetLastError())); - result = FullExePath; + return FullExePath; #endif /* _WIN32 */ - - return result; } /** @@ -217,6 +219,11 @@ bool Application::IsMainThread(void) return (boost::this_thread::get_id() == m_MainThreadID); } +void Application::SetMainThread(void) +{ + m_MainThreadID = boost::this_thread::get_id(); +} + #ifndef _WIN32 /** * Signal handler for SIGINT. Prepares the application for cleanly @@ -270,12 +277,6 @@ int Application::Run(int argc, char **argv) { int result; - assert(!Application::m_Instance); - - m_MainThreadID = boost::this_thread::get_id(); - - Application::m_Instance = GetSelf(); - #ifndef _WIN32 struct sigaction sa; memset(&sa, 0, sizeof(sa)); @@ -294,28 +295,24 @@ int Application::Run(int argc, char **argv) DynamicObject::BeginTx(); - if (IsDebugging()) { + try { result = Main(m_Arguments); - Application::m_Instance.reset(); - } else { - try { - result = Main(m_Arguments); + DynamicObject::FinishTx(); + DynamicObject::DeactivateObjects(); - Application::m_Instance.reset(); - } catch (const exception& ex) { - Application::m_Instance.reset(); + assert(m_Instance == NULL); + } catch (const exception& ex) { + Logger::Write(LogCritical, "base", "---"); + Logger::Write(LogCritical, "base", "Exception: " + Utility::GetTypeName(typeid(ex))); + Logger::Write(LogCritical, "base", "Message: " + String(ex.what())); - Logger::Write(LogCritical, "base", "---"); - Logger::Write(LogCritical, "base", "Exception: " + Utility::GetTypeName(typeid(ex))); - Logger::Write(LogCritical, "base", "Message: " + String(ex.what())); + if (IsDebugging()) + throw; - return EXIT_FAILURE; - } + return EXIT_FAILURE; } - DynamicObject::FinishTx(); - return result; } diff --git a/base/application.h b/base/application.h index 8288dde17..e91af95d8 100644 --- a/base/application.h +++ b/base/application.h @@ -29,12 +29,12 @@ class Component; * * @ingroup base */ -class I2_BASE_API Application : public Object { +class I2_BASE_API Application : public DynamicObject { public: typedef shared_ptr Ptr; typedef weak_ptr WeakPtr; - Application(void); + Application(const Dictionary::Ptr& serializedUpdate); ~Application(void); static Application::Ptr GetInstance(void); @@ -48,16 +48,18 @@ public: static bool IsDebugging(void); static bool IsMainThread(void); + static void SetMainThread(void); void UpdatePidFile(const String& filename); void ClosePidFile(void); + static String GetExePath(const String& argv0); + protected: void RunEventLoop(void); - String GetExePath(void) const; private: - static Application::Ptr m_Instance; /**< The application instance. */ + static Application *m_Instance; /**< The application instance. */ static bool m_ShuttingDown; /**< Whether the application is in the process of shutting down. */ diff --git a/base/dynamicobject.cpp b/base/dynamicobject.cpp index 13d82081d..b7faafccf 100644 --- a/base/dynamicobject.cpp +++ b/base/dynamicobject.cpp @@ -473,6 +473,11 @@ DynamicObject::ClassMap& DynamicObject::GetClasses(void) return classes; } +bool DynamicObject::ClassExists(const String& name) +{ + return (GetClasses().find(name) != GetClasses().end()); +} + void DynamicObject::RegisterClass(const String& type, DynamicObject::Factory factory) { if (GetObjects(type).first != GetObjects(type).second) diff --git a/base/dynamicobject.h b/base/dynamicobject.h index 025959b2f..c85a006ca 100644 --- a/base/dynamicobject.h +++ b/base/dynamicobject.h @@ -151,7 +151,8 @@ class RegisterClassHelper public: RegisterClassHelper(const String& name, DynamicObject::Factory factory) { - DynamicObject::RegisterClass(name, factory); + if (!DynamicObject::ClassExists(name)) + DynamicObject::RegisterClass(name, factory); } }; diff --git a/base/logger.cpp b/base/logger.cpp index 632442d6a..edd2a3d7c 100644 --- a/base/logger.cpp +++ b/base/logger.cpp @@ -110,13 +110,20 @@ LogSeverity Logger::GetMinSeverity(void) const */ void Logger::ForwardLogEntry(const LogEntry& entry) { + bool processed = false; + DynamicObject::Ptr object; BOOST_FOREACH(tie(tuples::ignore, object), DynamicObject::GetObjects("Logger")) { Logger::Ptr logger = dynamic_pointer_cast(object); if (entry.Severity >= logger->GetMinSeverity()) logger->m_Impl->ProcessLogEntry(entry); + + processed = true; } + + if (!processed && entry.Severity >= LogInformation) + StreamLogger::ProcessLogEntry(std::cout, entry); } String Logger::SeverityToString(LogSeverity severity) diff --git a/base/streamlogger.cpp b/base/streamlogger.cpp index fe249b30d..c35da60da 100644 --- a/base/streamlogger.cpp +++ b/base/streamlogger.cpp @@ -49,9 +49,10 @@ void StreamLogger::OpenFile(const String& filename) /** * Processes a log entry and outputs it to a stream. * + * @param stream The output stream. * @param entry The log entry. */ -void StreamLogger::ProcessLogEntry(const LogEntry& entry) +void StreamLogger::ProcessLogEntry(std::ostream& stream, const LogEntry& entry) { char timestamp[100]; @@ -60,7 +61,18 @@ void StreamLogger::ProcessLogEntry(const LogEntry& entry) strftime(timestamp, sizeof(timestamp), "%Y/%m/%d %H:%M:%S", &tmnow); - *m_Stream << "[" << timestamp << "] " + stream << "[" << timestamp << "] " << Logger::SeverityToString(entry.Severity) << "/" << entry.Facility << ": " << entry.Message << std::endl; } + +/** + * Processes a log entry and outputs it to a stream. + * + * @param entry The log entry. + */ +void StreamLogger::ProcessLogEntry(const LogEntry& entry) +{ + ProcessLogEntry(*m_Stream, entry); +} + diff --git a/base/streamlogger.h b/base/streamlogger.h index 1d7c99cfb..c06a4777c 100644 --- a/base/streamlogger.h +++ b/base/streamlogger.h @@ -18,6 +18,9 @@ public: ~StreamLogger(void); void OpenFile(const String& filename); + + static void ProcessLogEntry(std::ostream& stream, const LogEntry& entry); + protected: virtual void ProcessLogEntry(const LogEntry& entry); diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp index ce482686c..41b9f8b50 100644 --- a/icinga-app/icinga.cpp +++ b/icinga-app/icinga.cpp @@ -20,6 +20,9 @@ #include #ifndef _WIN32 +# include "icinga-version.h" +# define ICINGA_VERSION GIT_MESSAGE + # include #endif /* _WIN32 */ @@ -38,6 +41,48 @@ int main(int argc, char **argv) LTDL_SET_PRELOADED_SYMBOLS(); #endif /* _WIN32 */ - IcingaApplication::Ptr instance = boost::make_shared(); - return instance->Run(argc, argv); +#ifndef _WIN32 + lt_dlinit(); +#endif /* _WIN32 */ + + Application::SetMainThread(); + +#ifdef _WIN32 + Logger::Write(LogInformation, "icinga", "Icinga application loader"); +#else /* _WIN32 */ + Logger::Write(LogInformation, "icinga", "Icinga application loader (version: " ICINGA_VERSION ")"); +#endif /* _WIN32 */ + + if (argc < 3 || strcmp(argv[1], "-c") != 0) { + stringstream msgbuf; + msgbuf << "Syntax: " << argv[0] << " -c ..."; + Logger::Write(LogInformation, "base", msgbuf.str()); + return EXIT_FAILURE; + } + + String configFile = argv[2]; + + String componentDirectory = Utility::DirName(Application::GetExePath(argv[0])) + "/../lib/icinga2"; + Component::AddSearchDir(componentDirectory); + + DynamicObject::BeginTx(); + + /* load config file */ + vector configItems = ConfigCompiler::CompileFile(configFile); + + Logger::Write(LogInformation, "icinga", "Executing config items..."); + + BOOST_FOREACH(const ConfigItem::Ptr& item, configItems) { + item->Commit(); + } + + DynamicObject::FinishTx(); + + Application::Ptr app = Application::GetInstance(); + + if (!app) + throw_exception(runtime_error("Configuration must create an Application object.")); + + return app->Run(argc - 2, &(argv[2])); } + diff --git a/icinga/icingaapplication.cpp b/icinga/icingaapplication.cpp index 937fd2473..330e28d36 100644 --- a/icinga/icingaapplication.cpp +++ b/icinga/icingaapplication.cpp @@ -31,8 +31,25 @@ using namespace icinga; const String IcingaApplication::DefaultPidPath = "icinga.pid"; const String IcingaApplication::DefaultStatePath = "icinga.state"; -IcingaApplication::IcingaApplication(void) -{ } +IcingaApplication::IcingaApplication(const Dictionary::Ptr& serializedUpdate) + : Application(serializedUpdate) +{ + /* load cibsync config component */ + ConfigItemBuilder::Ptr cibsyncComponentConfig = boost::make_shared(); + cibsyncComponentConfig->SetType("Component"); + cibsyncComponentConfig->SetName("cibsync"); + cibsyncComponentConfig->SetLocal(true); + cibsyncComponentConfig->Compile()->Commit(); + cibsyncComponentConfig.reset(); + + /* load convenience config component */ + ConfigItemBuilder::Ptr convenienceComponentConfig = boost::make_shared(); + convenienceComponentConfig->SetType("Component"); + convenienceComponentConfig->SetName("convenience"); + convenienceComponentConfig->SetLocal(true); + convenienceComponentConfig->Compile()->Commit(); + convenienceComponentConfig.reset(); +} /** * The entry point for the Icinga application. @@ -42,26 +59,13 @@ IcingaApplication::IcingaApplication(void) */ int IcingaApplication::Main(const vector& args) { - /* create console logger */ - ConfigItemBuilder::Ptr consoleLogConfig = boost::make_shared(); - consoleLogConfig->SetType("Logger"); - consoleLogConfig->SetName("console"); - consoleLogConfig->SetLocal(true); - consoleLogConfig->AddExpression("type", OperatorSet, "console"); - consoleLogConfig->Compile()->Commit(); - consoleLogConfig.reset(); - -#ifdef _WIN32 - Logger::Write(LogInformation, "icinga", "Icinga component loader"); -#else /* _WIN32 */ - Logger::Write(LogInformation, "icinga", "Icinga component loader (version: " ICINGA_VERSION ")"); -#endif /* _WIN32 */ + Logger::Write(LogInformation, "icinga", "In IcingaApplication::Main()"); m_StartTime = Utility::GetTime(); - if (args.size() < 2) { + if (args.size() == 1 && args[0] == "--help") { stringstream msgbuf; - msgbuf << "Syntax: " << args[0] << " [-S] [-L logfile] [-d] [--] "; + msgbuf << "Syntax: " << args[0] << " ... -d"; Logger::Write(LogInformation, "icinga", msgbuf.str()); return EXIT_FAILURE; } @@ -92,68 +96,24 @@ int IcingaApplication::Main(const vector& args) throw_exception(invalid_argument("Unknown option: " + arg)); } } - - configFile = arg; - - if (it + 1 != args.end()) - throw_exception(invalid_argument("Trailing command line arguments after config filename.")); } - if (configFile.IsEmpty()) - throw_exception(invalid_argument("No config file was specified on the command line.")); + m_CertificateFile = Get("cert"); + m_CAFile = Get("ca"); + m_Node = Get("node"); + m_Service = Get("service"); - String componentDirectory = Utility::DirName(GetExePath()) + "/../lib/icinga2"; - Component::AddSearchDir(componentDirectory); - - /* load cibsync config component */ - ConfigItemBuilder::Ptr cibsyncComponentConfig = boost::make_shared(); - cibsyncComponentConfig->SetType("Component"); - cibsyncComponentConfig->SetName("cibsync"); - cibsyncComponentConfig->SetLocal(true); - cibsyncComponentConfig->Compile()->Commit(); - cibsyncComponentConfig.reset(); - - /* load convenience config component */ - ConfigItemBuilder::Ptr convenienceComponentConfig = boost::make_shared(); - convenienceComponentConfig->SetType("Component"); - convenienceComponentConfig->SetName("convenience"); - convenienceComponentConfig->SetLocal(true); - convenienceComponentConfig->Compile()->Commit(); - convenienceComponentConfig.reset(); - - /* load config file */ - vector configItems = ConfigCompiler::CompileFile(configFile); - - Logger::Write(LogInformation, "icinga", "Executing config items..."); - - BOOST_FOREACH(const ConfigItem::Ptr& item, configItems) { - item->Commit(); - } - - DynamicObject::Ptr icingaConfig = DynamicObject::GetObject("Application", "icinga"); - - if (!icingaConfig) - throw_exception(runtime_error("Configuration must contain an 'Application' object named 'icinga'.")); - - if (!icingaConfig->IsLocal()) - throw_exception(runtime_error("'icinga' application object must be 'local'.")); - - m_CertificateFile = icingaConfig->Get("cert"); - m_CAFile = icingaConfig->Get("ca"); - m_Node = icingaConfig->Get("node"); - m_Service = icingaConfig->Get("service"); - - m_PidPath = icingaConfig->Get("pidpath"); + m_PidPath = Get("pidpath"); if (m_PidPath.IsEmpty()) m_PidPath = DefaultPidPath; - m_StatePath = icingaConfig->Get("statepath"); + m_StatePath = Get("statepath"); if (m_StatePath.IsEmpty()) m_StatePath = DefaultStatePath; - m_Macros = icingaConfig->Get("macros"); + m_Macros = Get("macros"); - String logpath = icingaConfig->Get("logpath"); + String logpath = Get("logpath"); if (!logpath.IsEmpty()) { ConfigItemBuilder::Ptr fileLogConfig = boost::make_shared(); fileLogConfig->SetType("Logger"); diff --git a/icinga/icingaapplication.h b/icinga/icingaapplication.h index 91947d997..2045e1a4c 100644 --- a/icinga/icingaapplication.h +++ b/icinga/icingaapplication.h @@ -34,7 +34,7 @@ public: typedef shared_ptr Ptr; typedef weak_ptr WeakPtr; - IcingaApplication(void); + IcingaApplication(const Dictionary::Ptr& serializedUpdate); int Main(const vector& args); @@ -69,6 +69,8 @@ private: void DumpProgramState(void); }; +REGISTER_CLASS(IcingaApplication); + } #endif /* ICINGAAPPLICATION_H */