From da2dec447bc00ff2fd628b9c9ede12518c261965 Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Fri, 29 Apr 2022 06:17:16 +0200 Subject: [PATCH] allow to specify how many times we can try to connect to the database at startup add config options: db_startup_connection_max_attempts - default 0 (infinite) db_startup_connection_attempt_delay - delay in seconds between attempts (default 5) BREAKING CHANGE: WINIX_PLUGIN_INIT plugin message requires to set result status, you have to set the result status to true (env.res) if your plugin was initialized correctly, otherwise winix will not start --- winixd/core/app.cpp | 51 ++++++++++++++++++++++++---- winixd/core/app.h | 5 ++- winixd/core/config.cpp | 3 ++ winixd/core/config.h | 11 +++++- winixd/core/pluginmsg.h | 2 ++ winixd/core/system.cpp | 15 ++++---- winixd/core/system.h | 2 +- winixd/core/threadmanager.cpp | 26 +++++++++----- winixd/core/threadmanager.h | 8 ++--- winixd/db/dbconn.cpp | 43 +++++++++++++++++++++-- winixd/db/dbconn.h | 3 +- winixd/main/main.cpp | 4 --- winixd/notify/notify.cpp | 8 +++-- winixd/notify/notify.h | 2 +- winixd/plugins/export/init.cpp | 2 +- winixd/plugins/mailregister/init.cpp | 1 + winixd/plugins/thread/init.cpp | 8 ++++- winixd/plugins/ticket/init.cpp | 1 + 18 files changed, 154 insertions(+), 41 deletions(-) diff --git a/winixd/core/app.cpp b/winixd/core/app.cpp index 2182962..c7af292 100644 --- a/winixd/core/app.cpp +++ b/winixd/core/app.cpp @@ -178,7 +178,7 @@ Log & App::GetMainLog() -void App::InitPlugins() +void App::LoadPlugins() { plugin.LoadPlugins(config.plugins_dir, config.plugin_file); } @@ -350,8 +350,26 @@ bool App::TryToMakeDatabaseMigration() } +bool App::InitializePlugins() +{ + PluginRes plugin_res = plugin.Call((Session*)0, WINIX_PLUGIN_INIT); + + if( plugin_res.res_false > 0 ) + { + log << log1 << "App: " << plugin_res.res_false << " plugin(s) cannot initialize itself, exiting" << logend; + return false; + } + + return true; +} + + bool App::Init() { + // load plugins before loading sessions - session_manager.LoadSessions() + // because some of the plugins can init its own sessions dates + LoadPlugins(); + if( !config.db_conn_string.empty() ) postgresql_connector.set_conn_param(config.db_conn_string); else @@ -359,7 +377,11 @@ bool App::Init() postgresql_connector.set_logger(log); postgresql_connector.set_log_queries(config.log_db_query); - postgresql_connector.wait_for_connection(); + + if( !postgresql_connector.wait_for_connection(config.db_startup_connection_max_attempts, config.db_startup_connection_attempt_delay) ) + { + return false; + } model_connector.set_flat_connector(json_connector); model_connector.set_db_connector(postgresql_connector); @@ -378,6 +400,7 @@ bool App::Init() model_connector.set_winix_time_zones(&system.time_zones); model_connector.set_winix_pattern_cacher(&TemplatesFunctions::pattern_cacher); + // CHECKME this will call WINIX_MAKE_DATABASE_MIGRATION, but WINIX_PLUGIN_INIT was not called yet, it is correct? if( !TryToMakeDatabaseMigration() ) return false; @@ -387,13 +410,17 @@ bool App::Init() else db_conn.SetConnParam(config.db_host, config.db_hostaddr, config.db_port, config.db_database, config.db_user, config.db_pass); - db_conn.WaitForConnection(); + if( !db_conn.WaitForConnection(config.db_startup_connection_max_attempts, config.db_startup_connection_attempt_delay) ) + return false; + db.LogQueries(config.log_db_query); cur.request->Clear(); compress.set_dependency(&winix_base); compress.Init(); - system.Init(); + + if( !system.Init() ) + return false; functions.Init(); templates.Init(); // init templates after functions are created @@ -414,9 +441,10 @@ bool App::Init() cookie_parser.set_dependency(&winix_model); - plugin.Call((Session*)0, WINIX_PLUGIN_INIT); + if( !AddSystemThreads() ) + return false; -return true; + return InitializePlugins(); } @@ -2788,6 +2816,15 @@ int sig; } +bool App::AddSystemThreads() +{ + bool ok = true; + + ok = ok && system.thread_manager.Add(&session_manager, L"session_manager"); + + return ok; +} + void App::StartThreads() { @@ -2797,7 +2834,7 @@ void App::StartThreads() // special thread only for signals pthread_create(&signal_thread, 0, SpecialThreadForSignals, this); - system.thread_manager.Add(&session_manager, L"session_manager"); + // start all threads from thread manager system.thread_manager.StartAll(); } diff --git a/winixd/core/app.h b/winixd/core/app.h index 496cbaf..df897eb 100644 --- a/winixd/core/app.h +++ b/winixd/core/app.h @@ -74,7 +74,8 @@ public: bool DropPrivileges(); void InitLoggers(); Log & GetMainLog(); - void InitPlugins(); + void LoadPlugins(); + bool InitializePlugins(); bool Init(); void Start(); void Close(); @@ -188,6 +189,8 @@ private: // file logger, one object for all Log objects FileLog file_log; + bool AddSystemThreads(); + bool TranslateFCGInames(char * sock, char * sock_user, char * sock_group); bool InitFCGIChmodChownSocket(char * sock, char * sock_user, char * sock_group); bool DropPrivileges(char * user, char * group); diff --git a/winixd/core/config.cpp b/winixd/core/config.cpp index b758c19..98643f2 100644 --- a/winixd/core/config.cpp +++ b/winixd/core/config.cpp @@ -187,9 +187,12 @@ void Config::AssignValues() db_database = Text(L"db_database"); db_user = Text(L"db_user"); db_pass = Text(L"db_pass"); + db_startup_connection_max_attempts = Size(L"db_startup_connection_max_attempts", 0); + db_startup_connection_attempt_delay = Size(L"db_startup_connection_attempt_delay", 5); db_make_migration_if_needed = Bool(L"db_make_migration_if_needed", true); db_stop_if_migration_fails = Bool(L"db_stop_if_migration_fails", true); + item_url_empty = Text(L"item_url_empty"); url_proto = Text(L"url_proto", L"http://"); diff --git a/winixd/core/config.h b/winixd/core/config.h index ab1bc69..f3a3269 100644 --- a/winixd/core/config.h +++ b/winixd/core/config.h @@ -242,12 +242,21 @@ public: std::wstring db_user; std::wstring db_pass; + // specify how many times we can try to connect to the database at startup + // default 0 (infinite) + size_t db_startup_connection_max_attempts; + + // delay between each connection attempt at startup + // default 5 (seconds) + size_t db_startup_connection_attempt_delay; + // make database migration if needed - // bool db_make_migration_if_needed; + // if a migration fails then stop winix bool db_stop_if_migration_fails; + // the name of the cookie which has the session identifier std::wstring http_session_id_name; diff --git a/winixd/core/pluginmsg.h b/winixd/core/pluginmsg.h index fbdd42e..d04bc08 100644 --- a/winixd/core/pluginmsg.h +++ b/winixd/core/pluginmsg.h @@ -144,6 +144,8 @@ namespace Winix // winix is initialized, // now you can initialize your plugin // session pointer is null +// you have to set the result status to true (env.res) if your +// plugin was initialized correctly, otherwise winix will not start #define WINIX_PLUGIN_INIT 30080 // winix is ready to remove a plugin diff --git a/winixd/core/system.cpp b/winixd/core/system.cpp index 6171b63..1408d08 100644 --- a/winixd/core/system.cpp +++ b/winixd/core/system.cpp @@ -124,7 +124,7 @@ void System::ReadTimeZones() } -void System::Init() +bool System::Init() { //thread_manager.SetSynchro(synchro); thread_manager.Init(); @@ -157,21 +157,24 @@ void System::Init() notify.SetUsers(&users); notify.SetDirs(&dirs); notify.SetThreadManager(&thread_manager); - notify.Init(); + + if( !notify.Init() ) + return false; image.SetDb(db); image.SetConfig(config); image.SetSystem(this); - thread_manager.Add(&image, L"image"); - - + if( !thread_manager.Add(&image, L"image") ) + return false; // SetSynchro will be called by ThreadManager itself // job.ReadFromFile(); - thread_manager.Add(&job, L"job"); + if( !thread_manager.Add(&job, L"job") ) + return false; ReadTimeZones(); + return true; } diff --git a/winixd/core/system.h b/winixd/core/system.h index 1fe3c47..12ea422 100644 --- a/winixd/core/system.h +++ b/winixd/core/system.h @@ -120,7 +120,7 @@ public: void set_dependency(WinixModelDeprecated * winix_model); - void Init(); + bool Init(); void AddParams(const ParamTab & param_tab, std::wstring & str, bool clear_str = true); diff --git a/winixd/core/threadmanager.cpp b/winixd/core/threadmanager.cpp index 41dc65c..3347969 100644 --- a/winixd/core/threadmanager.cpp +++ b/winixd/core/threadmanager.cpp @@ -70,7 +70,7 @@ sigset_t set; -void ThreadManager::Add(BaseThread * pbase, const wchar_t * thread_name) +bool ThreadManager::Add(BaseThread * pbase, const wchar_t * thread_name) { thread_tab.emplace_back(); ThreadItem & item = thread_tab.back(); @@ -96,7 +96,14 @@ void ThreadManager::Add(BaseThread * pbase, const wchar_t * thread_name) data.postgresql_connector.set_logger(item.object->get_logger()); data.postgresql_connector.set_log_queries(config->log_db_query); - data.postgresql_connector.wait_for_connection(); + + if( !data.postgresql_connector.wait_for_connection(config->db_startup_connection_max_attempts, config->db_startup_connection_attempt_delay) ) + { + Log * plog = item.object->get_logger(); + (*plog) << logsave; + return false; + } + data.model_connector.set_db_connector(data.postgresql_connector); data.model_connector.set_flat_connector(data.json_connector); data.model_connector.set_logger(item.object->get_logger()); @@ -125,23 +132,26 @@ void ThreadManager::Add(BaseThread * pbase, const wchar_t * thread_name) log << log4 << "TM: added a thread to the queue, number: " << (thread_tab.size()-1) << ", name: " << thread_name << logend; } + + return true; } -void ThreadManager::Add(BaseThread & pbase, const wchar_t * thread_name) +bool ThreadManager::Add(BaseThread & pbase, const wchar_t * thread_name) { - Add(&pbase, thread_name); + return Add(&pbase, thread_name); } -void ThreadManager::Add(BaseThread * pbase, const std::wstring & thread_name) +bool ThreadManager::Add(BaseThread * pbase, const std::wstring & thread_name) { - Add(pbase, thread_name.c_str()); + return Add(pbase, thread_name.c_str()); } -void ThreadManager::Add(BaseThread & pbase, const std::wstring & thread_name) + +bool ThreadManager::Add(BaseThread & pbase, const std::wstring & thread_name) { - Add(&pbase, thread_name.c_str()); + return Add(&pbase, thread_name.c_str()); } diff --git a/winixd/core/threadmanager.h b/winixd/core/threadmanager.h index b0c0d81..8390d1d 100644 --- a/winixd/core/threadmanager.h +++ b/winixd/core/threadmanager.h @@ -59,10 +59,10 @@ public: // adding a new thread to the queue // the thread will be running only if we call StartAll() before // otherwise the thread will be waiting for StartAll() - void Add(BaseThread * pbase, const wchar_t * thread_name); - void Add(BaseThread & pbase, const wchar_t * thread_name); - void Add(BaseThread * pbase, const std::wstring & thread_name); - void Add(BaseThread & pbase, const std::wstring & thread_name); + bool Add(BaseThread * pbase, const wchar_t * thread_name); + bool Add(BaseThread & pbase, const wchar_t * thread_name); + bool Add(BaseThread * pbase, const std::wstring & thread_name); + bool Add(BaseThread & pbase, const std::wstring & thread_name); // starting all threads void StartAll(); diff --git a/winixd/db/dbconn.cpp b/winixd/db/dbconn.cpp index a1ff3ee..a0bc248 100644 --- a/winixd/db/dbconn.cpp +++ b/winixd/db/dbconn.cpp @@ -130,26 +130,63 @@ void DbConn::Connect() } +void DbConn::LogNoConnection(size_t attempts) +{ + log << log2 << "Db: connection to the database cannot be established"; + log << ", (" << attempts << " attempt(s))" << logend; + log << logsave; +} + void DbConn::LogConnectionSocket() { log << log2 << "Db: connection to the database works fine" << logend; log << log3 << "Db: connection socket: " << PQsocket(pg_conn) << logend; + log << logsave; } -void DbConn::WaitForConnection() +bool DbConn::WaitForConnection(size_t attempts_max, size_t attempt_delay) { + size_t attempts = 0; + bool attempts_exceeded = false; + + if( attempt_delay == 0 ) + attempt_delay = 1; + + if( attempt_delay > 120 ) + attempt_delay = 120; + if( !pg_conn || PQstatus(pg_conn) != CONNECTION_OK ) { log << log3 << "Db: waiting for the db to be ready...." << logend << logsave; - while( !AssertConnection(false, false) ) - sleep(5); + while( !attempts_exceeded && !AssertConnection(false, false) ) + { + if( attempts_max != 0 ) + { + attempts += 1; + attempts_exceeded = (attempts >= attempts_max); + } + if( !attempts_exceeded ) + { + sleep(attempt_delay); + } + } + } + + if( attempts_exceeded ) + { + LogNoConnection(attempts); + } + else + { LogConnectionSocket(); } + + return !attempts_exceeded; } diff --git a/winixd/db/dbconn.h b/winixd/db/dbconn.h index 4fc9093..4222f7a 100644 --- a/winixd/db/dbconn.h +++ b/winixd/db/dbconn.h @@ -57,7 +57,7 @@ public: void SetConnParam(const std::wstring & host, const std::wstring & hostaddr, const std::wstring & port, const std::wstring & database, const std::wstring & user, const std::wstring & pass); void Connect(); - void WaitForConnection(); + bool WaitForConnection(size_t attempts_max, size_t attempt_delay); void Close(); bool AssertConnection(bool put_log = true, bool throw_if_no_connection = true); void SetDbParameters(); @@ -65,6 +65,7 @@ public: private: + void LogNoConnection(size_t attempts); void LogConnectionSocket(); PGconn * pg_conn; diff --git a/winixd/main/main.cpp b/winixd/main/main.cpp index 8aee640..979d892 100644 --- a/winixd/main/main.cpp +++ b/winixd/main/main.cpp @@ -353,10 +353,6 @@ using Winix::app; app.LogUserGroups(); Winix::SavePidFile(log); - // load plugins before loading sessions - session_manager.LoadSessions() - // because some of the plugins can init its own sessions dates - app.InitPlugins(); - // app.Init() starts other threads as well (they will be waiting on the lock) if( !app.Init() ) { diff --git a/winixd/notify/notify.cpp b/winixd/notify/notify.cpp index 3a3a61c..9dc7c2f 100644 --- a/winixd/notify/notify.cpp +++ b/winixd/notify/notify.cpp @@ -85,14 +85,16 @@ void Notify::SetThreadManager(ThreadManager * pmanager) -void Notify::Init() +bool Notify::Init() { //notify_thread.SetConfig(config); notify_thread.SetUsers(users); notify_thread.SetNotifyPool(¬ify_pool); notify_thread.SetPatterns(&patterns); //notify_thread.SetFileLog(file_log); - thread_manager->Add(¬ify_thread, L"notifications"); + + if( !thread_manager->Add(¬ify_thread, L"notifications") ) + return false; patterns.SetDirectories(config->txt_templates_dir, config->txt_templates_dir_default); patterns.SetLocale(&TemplatesFunctions::locale); @@ -103,6 +105,8 @@ void Notify::Init() notify_template_reset_password = AddTemplate(L"notify_reset_password.txt"); plugin->Call((Session*)0, WINIX_NOTIFY_ADD_TEMPLATE); + + return true; } diff --git a/winixd/notify/notify.h b/winixd/notify/notify.h index c45881a..a8260b8 100644 --- a/winixd/notify/notify.h +++ b/winixd/notify/notify.h @@ -70,7 +70,7 @@ public: void SetThreadManager(ThreadManager * pmanager); //void SetFileLog(FileLog * file_log); - void Init(); + bool Init(); size_t AddTemplate(const std::wstring & file_name); void ReadTemplates(); diff --git a/winixd/plugins/export/init.cpp b/winixd/plugins/export/init.cpp index 8f5d273..3c71b77 100644 --- a/winixd/plugins/export/init.cpp +++ b/winixd/plugins/export/init.cpp @@ -87,7 +87,7 @@ void InitPlugin(PluginInfo & info) { export_thread.SetBaseUrl(info.config->base_url); - info.system->thread_manager.Add(&export_thread, L"export"); + info.res = info.system->thread_manager.Add(&export_thread, L"export"); export_info.ReadExportDirs(); } diff --git a/winixd/plugins/mailregister/init.cpp b/winixd/plugins/mailregister/init.cpp index 7fbafa9..5675d40 100644 --- a/winixd/plugins/mailregister/init.cpp +++ b/winixd/plugins/mailregister/init.cpp @@ -156,6 +156,7 @@ void ProcessRequest(PluginInfo & info) void InitPlugin(PluginInfo & info) { ConfigReload(info); + info.res = true; } diff --git a/winixd/plugins/thread/init.cpp b/winixd/plugins/thread/init.cpp index 6b37f1e..1a1a39b 100644 --- a/winixd/plugins/thread/init.cpp +++ b/winixd/plugins/thread/init.cpp @@ -176,6 +176,12 @@ void Rescan(PluginInfo & info) } } +void PluginInit(PluginInfo & info) +{ + Rescan(info); + info.res = true; +} + } // namespace @@ -202,7 +208,7 @@ using namespace Thread; info.plugin->Assign(WINIX_PL_THREAD_PREPARE_THREAD, PrepareThread); // temporarily - info.plugin->Assign(WINIX_PLUGIN_INIT, Rescan); + info.plugin->Assign(WINIX_PLUGIN_INIT, PluginInit); tdb.SetConn(info.db->GetConn()); tdb.LogQueries(info.config->log_db_query); diff --git a/winixd/plugins/ticket/init.cpp b/winixd/plugins/ticket/init.cpp index bd51a84..c7294cd 100644 --- a/winixd/plugins/ticket/init.cpp +++ b/winixd/plugins/ticket/init.cpp @@ -130,6 +130,7 @@ void RemoveTicket(PluginInfo & info) void InitTicket(PluginInfo & info) { ticket_info.progress_prefix = info.config->Text(L"ticket_form_progress_prefix", L"progress"); + info.res = true; }