anope/src/main.cpp

219 lines
5.2 KiB
C++

/* 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 <limits.h>
#else
#include <process.h>
#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<time_t>("updatetimeout", "5m"));
ExpireTimer expireTimer(Config->GetBlock("options")->Get<time_t>("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<char *>(sbin.c_str());
execve(Anope::ServicesBin.c_str(), av, envp);
Log() << "Restart failed";
Anope::ReturnValue = -1;
}
return Anope::ReturnValue;
}