/* Services -- main source file. * * (C) 2003-2022 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. * * Based on the original code of Epona by Lara. * Based on the original code of Services by Andy Church. */ #include "services.h" #include "timers.h" #include "config.h" #include "bots.h" #include "socketengine.h" #include "uplink.h" #ifndef _WIN32 #include #else #include #endif /* Command-line options: */ int Anope::Debug = 0; bool Anope::ReadOnly = false, Anope::NoFork = false, Anope::NoThird = false, Anope::NoExpire = false, Anope::ProtocolDebug = false; Anope::string Anope::ServicesDir; Anope::string Anope::ServicesBin; int Anope::ReturnValue = 0; sig_atomic_t Anope::Signal = 0; bool Anope::Quitting = false; bool Anope::Restarting = false; Anope::string Anope::QuitReason; static Anope::string BinaryDir; /* Full path to services bin directory */ time_t Anope::StartTime = time(NULL); time_t Anope::CurTime = time(NULL); int Anope::CurrentUplink = -1; class UpdateTimer : public Timer { public: UpdateTimer(time_t timeout) : Timer(timeout, Anope::CurTime, true) { } void Tick(time_t) anope_override { Anope::SaveDatabases(); } }; class ExpireTimer : public Timer { public: ExpireTimer(time_t timeout) : Timer(timeout, Anope::CurTime, true) { } void Tick(time_t) anope_override { FOREACH_MOD(OnExpireTick, ()); } }; void Anope::SaveDatabases() { if (Anope::ReadOnly) return; Log(LOG_DEBUG) << "Saving databases"; FOREACH_MOD(OnSaveDatabase, ()); } /** The following comes from InspIRCd to get the full path of the Anope executable */ static Anope::string GetFullProgDir(const Anope::string &argv0) { char buffer[PATH_MAX]; #ifdef _WIN32 /* Windows has specific API calls to get the EXE path that never fail. * For once, Windows has something of use, compared to the POSIX code * for this, this is positively neato. */ if (GetModuleFileName(NULL, buffer, PATH_MAX)) { Anope::string fullpath = buffer; Anope::string::size_type n = fullpath.rfind("\\"); Anope::ServicesBin = fullpath.substr(n + 1, fullpath.length()); return fullpath.substr(0, n); } #else // Get the current working directory if (getcwd(buffer, PATH_MAX)) { Anope::string remainder = argv0; Anope::ServicesBin = remainder; Anope::string::size_type n = Anope::ServicesBin.rfind("/"); Anope::string fullpath; if (Anope::ServicesBin[0] == '/') fullpath = Anope::ServicesBin.substr(0, n); else fullpath = Anope::string(buffer) + "/" + Anope::ServicesBin.substr(0, n); Anope::ServicesBin = Anope::ServicesBin.substr(n + 1, remainder.length()); return fullpath; } #endif return "/"; } /* Main routine. (What does it look like? :-) ) */ int main(int ac, char **av, char **envp) { /* String comparisons won't work until we build the case map cache, so do it first */ Anope::CaseMapRebuild(); BinaryDir = GetFullProgDir(av[0]); if (BinaryDir[BinaryDir.length() - 1] == '.') BinaryDir = BinaryDir.substr(0, BinaryDir.length() - 2); #ifdef _WIN32 Anope::string::size_type n = BinaryDir.rfind('\\'); #else Anope::string::size_type n = BinaryDir.rfind('/'); #endif Anope::ServicesDir = BinaryDir.substr(0, n); #ifdef _WIN32 /* Clean out the module runtime directory prior to running, just in case files were left behind during a previous run */ ModuleManager::CleanupRuntimeDirectory(); OnStartup(); #endif try { /* General initialization first */ Anope::Init(ac, av); } catch (const CoreException &ex) { Log() << ex.GetReason(); return -1; } try { Uplink::Connect(); } catch (const SocketException &ex) { Log(LOG_TERMINAL) << "Unable to connect to uplink #" << (Anope::CurrentUplink + 1) << " (" << Config->Uplinks[Anope::CurrentUplink].host << ":" << Config->Uplinks[Anope::CurrentUplink].port << "): " << ex.GetReason(); } /* Set up timers */ time_t last_check = Anope::CurTime; UpdateTimer updateTimer(Config->GetBlock("options")->Get("updatetimeout", "5m")); ExpireTimer expireTimer(Config->GetBlock("options")->Get("expiretimeout", "30m")); /*** Main loop. ***/ while (!Anope::Quitting) { Log(LOG_DEBUG_2) << "Top of main loop"; /* Process timers */ if (Anope::CurTime - last_check >= Config->TimeoutCheck) { TimerManager::TickTimers(Anope::CurTime); last_check = Anope::CurTime; } /* Process the socket engine */ SocketEngine::Process(); if (Anope::Signal) Anope::HandleSignal(); } if (Anope::Restarting) { FOREACH_MOD(OnRestart, ()); } else { FOREACH_MOD(OnShutdown, ()); } if (Anope::QuitReason.empty()) Anope::QuitReason = "Terminating, reason unknown"; Log() << Anope::QuitReason; delete UplinkSock; ModuleManager::UnloadAll(); SocketEngine::Shutdown(); for (Module *m; (m = ModuleManager::FindFirstOf(PROTOCOL)) != NULL;) ModuleManager::UnloadModule(m, NULL); #ifdef _WIN32 ModuleManager::CleanupRuntimeDirectory(); OnShutdown(); #endif if (Anope::Restarting) { chdir(BinaryDir.c_str()); Anope::string sbin = "./" + Anope::ServicesBin; av[0] = const_cast(sbin.c_str()); execve(Anope::ServicesBin.c_str(), av, envp); Log() << "Restart failed"; Anope::ReturnValue = -1; } return Anope::ReturnValue; }