summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAdam <Adam@anope.org>2011-05-21 04:57:27 -0400
committerAdam <Adam@anope.org>2011-05-21 04:57:27 -0400
commit115f94bfc23c481eab50298895f76bd494179cd8 (patch)
treeeeda28c466e9f2f532b2dee75cb817eba9066357 /src
parent7e5727288d9b2da5b53476b91f8cb9fe96e14d53 (diff)
Made Anope able to process normally when disconnected from the uplink and not sleep(), enable usage of non-blocking connect() and default all sockets to non blocking mode. Some cleanup to m_ssl and some cleanup to main.cpp.
Diffstat (limited to 'src')
-rw-r--r--src/config.cpp3
-rw-r--r--src/dns.cpp23
-rw-r--r--src/init.cpp1
-rw-r--r--src/main.cpp444
-rw-r--r--src/messages.cpp2
-rw-r--r--src/misc.cpp9
-rw-r--r--src/modulemanager.cpp8
-rw-r--r--src/socketengines/socketengine_epoll.cpp2
-rw-r--r--src/socketengines/socketengine_poll.cpp2
-rw-r--r--src/socketengines/socketengine_select.cpp2
-rw-r--r--src/sockets.cpp132
11 files changed, 327 insertions, 301 deletions
diff --git a/src/config.cpp b/src/config.cpp
index 671cf3ed7..7d3efcb91 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -563,7 +563,7 @@ bool InitUplinks(ServerConfig *config, const Anope::string &)
{
if (!config->Uplinks.empty())
{
- std::list<Uplink *>::iterator curr_uplink = config->Uplinks.begin(), end_uplink = config->Uplinks.end();
+ std::vector<Uplink *>::iterator curr_uplink = config->Uplinks.begin(), end_uplink = config->Uplinks.end();
for (; curr_uplink != end_uplink; ++curr_uplink)
delete *curr_uplink;
}
@@ -1033,7 +1033,6 @@ ConfigItems::ConfigItems(ServerConfig *conf)
{"options", "mlock", "+nrt", new ValueContainerString(&conf->MLock), DT_STRING, NoValidation},
{"options", "nomlock", "", new ValueContainerString(&conf->NoMLock), DT_STRING, NoValidation},
{"options", "botmodes", "", new ValueContainerString(&conf->BotModes), DT_STRING, NoValidation},
- {"options", "maxretries", "10", new ValueContainerUInt(&conf->MaxRetries), DT_UINTEGER, NoValidation},
{"options", "retrywait", "60", new ValueContainerInt(&conf->RetryWait), DT_INTEGER, ValidateNotZero},
{"options", "hideprivilegedcommands", "no", new ValueContainerBool(&conf->HidePrivilegedCommands), DT_BOOLEAN, NoValidation},
{"nickserv", "nick", "NickServ", new ValueContainerString(&conf->s_NickServ), DT_STRING | DT_NORELOAD, ValidateNotEmpty},
diff --git a/src/dns.cpp b/src/dns.cpp
index 7475545c0..4c1178865 100644
--- a/src/dns.cpp
+++ b/src/dns.cpp
@@ -42,6 +42,11 @@ void DNSRequest::Process()
{
Log(LOG_DEBUG_2) << "Resolver: Processing request to lookup " << this->address << ", of type " << this->QT;
+ if (!DNSEngine)
+ throw SocketException("DNSEngine has not been initialized");
+ else if (!DNSEngine->sock || !DNSEngine->sock->connected)
+ throw SocketException("Connection to nameserver has not been established");
+
if (this->use_cache && DNSEngine->CheckCache(this))
{
Log(LOG_DEBUG_2) << "Resolver: Using cached result";
@@ -236,7 +241,8 @@ DNSSocket::~DNSSocket()
delete DNSEngine->packets[i - 1];
DNSEngine->packets.clear();
Log(LOG_NORMAL, "dns") << "Resolver: Lost connection to nameserver";
- DNSEngine->sock = NULL;
+ if (DNSEngine)
+ DNSEngine->sock = NULL;
}
int DNSSocket::SendTo(const unsigned char *buf, size_t len) const
@@ -497,7 +503,10 @@ bool DNSSocket::ProcessRead()
bool DNSSocket::ProcessWrite()
{
- Log(LOG_DEBUG_2) << "Resolver: Writing to UDP socket";
+ if (!this->connected)
+ return ConnectionSocket::ProcessWrite();
+
+ Log(LOG_DEBUG_2) << "Resolver: Writing to DNS socket";
bool cont = true;
for (unsigned i = DNSEngine->packets.size(); cont && i > 0; --i)
@@ -517,6 +526,16 @@ bool DNSSocket::ProcessWrite()
return cont;
}
+void DNSSocket::OnConnect()
+{
+ Log(LOG_DEBUG_2) << "Resolver: Successfully connected to nameserver";
+}
+
+void DNSSocket::OnError(const Anope::string &error)
+{
+ Log() << "Resolver: Error connecting to nameserver: " << error;
+}
+
DNSManager::DNSManager() : Timer(300, Anope::CurTime, true)
{
this->sock = NULL;
diff --git a/src/init.cpp b/src/init.cpp
index ad95e4d4b..7fa684336 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -376,7 +376,6 @@ void Init(int ac, char **av)
/* Announce ourselves to the logfile. */
Log() << "Anope " << Anope::Version() << " starting up" << (debug || readonly ? " (options:" : "") << (debug ? " debug" : "") << (readonly ? " readonly" : "") << (debug || readonly ? ")" : "");
- start_time = Anope::CurTime;
/* Set signal handlers. Catch certain signals to let us do things or
* panic as necessary, and ignore all others.
diff --git a/src/main.cpp b/src/main.cpp
index 380240dbb..199b3162f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -55,8 +55,8 @@ Anope::string binary_dir; /* Used to store base path for Anope */
/* Set to 1 if we are to quit */
bool quitting = false;
-/* Set to 1 if we are to quit after saving databases */
-bool shutting_down = false;
+/* Set to true if we are restarting */
+bool restarting = false;
/* Contains a message as to why services is terminating */
Anope::string quitmsg;
@@ -65,10 +65,7 @@ Anope::string quitmsg;
bool save_data = false;
/* At what time were we started? */
-time_t start_time;
-
-/* Parameters and environment */
-char **my_av, **my_envp;
+time_t start_time = time(NULL);
time_t Anope::CurTime = time(NULL);
@@ -92,123 +89,125 @@ class UpdateTimer : public Timer
};
ConnectionSocket *UplinkSock = NULL;
+int CurrentUplink = 0;
-UplinkSocket::UplinkSocket(bool ipv6) : ConnectionSocket(ipv6)
-{
- UplinkSock = this;
-}
+static void Connect();
-UplinkSocket::~UplinkSocket()
+class ReconnectTimer : public Timer
{
- SocketEngine::Process();
- UplinkSock = NULL;
-}
+ public:
+ ReconnectTimer(int wait) : Timer(wait) { }
-bool UplinkSocket::Read(const Anope::string &buf)
+ void Tick(time_t)
+ {
+ Connect();
+ }
+};
+
+class UplinkSocket : public ConnectionSocket
{
- process(buf);
- return true;
-}
+ public:
+ UplinkSocket() : ConnectionSocket(Config->Uplinks[CurrentUplink]->ipv6)
+ {
+ UplinkSock = this;
+ }
-/*************************************************************************/
+ ~UplinkSocket()
+ {
+ if (Me && Me->GetUplink() && Me->GetUplink()->IsSynced())
+ {
+ FOREACH_MOD(I_OnServerDisconnect, OnServerDisconnect());
-void save_databases()
-{
- if (readonly)
- return;
+ /* Send a quit for all of our bots */
+ for (Anope::insensitive_map<BotInfo *>::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it)
+ {
+ BotInfo *bi = it->second;
+
+ /* Don't use quitmsg here, it may contain information you don't want people to see */
+ ircdproto->SendQuit(bi, "Shutting down");
+ /* Erase bots from the user list so they don't get nuked later on */
+ UserListByNick.erase(bi->nick);
+ if (!bi->GetUID().empty())
+ UserListByUID.erase(bi->GetUID());
+ }
- EventReturn MOD_RESULT;
- FOREACH_RESULT(I_OnSaveDatabase, OnSaveDatabase());
- Log(LOG_DEBUG) << "Saving databases";
-}
+ /* Clear all of our users, but not our bots */
+ for (Anope::insensitive_map<User *>::const_iterator it = UserListByNick.begin(); it != UserListByNick.end();)
+ {
+ User *u = it->second;
+ ++it;
-/*************************************************************************/
+ if (u->server != Me)
+ delete u;
+ }
-/* Restarts services */
-void do_restart_services()
-{
- if (!readonly)
- {
- save_databases();
- }
- Log() << "Restarting";
+ ircdproto->SendSquit(Config->ServerName, quitmsg);
- if (quitmsg.empty())
- quitmsg = "Restarting";
+ this->ProcessWrite(); // Write out the last bit
+ }
- FOREACH_MOD(I_OnRestart, OnRestart());
- ModuleManager::UnloadAll();
+ Me->SetFlag(SERVER_SYNCING);
+ for (unsigned i = Me->GetLinks().size(); i > 0; --i)
+ if (!Me->GetLinks()[i - 1]->HasFlag(SERVER_JUPED))
+ delete Me->GetLinks()[i - 1];
- /* Send a quit for all of our bots */
- for (Anope::insensitive_map<BotInfo *>::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it)
- {
- BotInfo *bi = it->second;
-
- /* Don't use quitmsg here, it may contain information you don't want people to see */
- ircdproto->SendQuit(bi, "Restarting");
- /* Erase bots from the user list so they don't get nuked later on */
- UserListByNick.erase(bi->nick);
- if (!bi->GetUID().empty())
- UserListByUID.erase(bi->GetUID());
- }
+ UplinkSock = NULL;
- ircdproto->SendSquit(Config->ServerName, quitmsg);
- delete UplinkSock;
- SocketEngine::Shutdown();
+ if (!quitting)
+ {
+ int Retry = Config->RetryWait;
+ if (Retry <= 0)
+ Retry = 60;
- chdir(binary_dir.c_str());
- my_av[0] = const_cast<char *>(("./" + services_bin).c_str());
- execve(services_bin.c_str(), my_av, my_envp);
- if (!readonly)
- {
- throw FatalException("Restart failed");
+ Log() << "Retrying in " << Retry << " seconds";
+ new ReconnectTimer(Retry);
+ }
}
- exit(1);
-}
+ bool Read(const Anope::string &buf)
+ {
+ process(buf);
+ return true;
+ }
-/*************************************************************************/
+ void OnConnect()
+ {
+ Log() << "Successfully connected to " << Config->Uplinks[CurrentUplink]->host << ":" << Config->Uplinks[CurrentUplink]->port;
+ ircdproto->SendConnect();
+ FOREACH_MOD(I_OnServerConnect, OnServerConnect());
+ }
-/* Terminates services */
+ void OnError(const Anope::string &error)
+ {
+ Log() << "Unable to connect to server " << Config->Uplinks[CurrentUplink]->host << ":" << Config->Uplinks[CurrentUplink]->port << (!error.empty() ? (": " + error) : "");
+ this->SetFlag(SF_DEAD);
+ }
+};
-static void services_shutdown()
+static void Connect()
{
- if (quitmsg.empty())
- quitmsg = "Terminating, reason unknown";
- Log() << quitmsg;
+ if (static_cast<unsigned>(++CurrentUplink) >= Config->Uplinks.size())
+ CurrentUplink = 0;
- FOREACH_MOD(I_OnShutdown, OnShutdown());
- ModuleManager::UnloadAll();
+ Uplink *u = Config->Uplinks[CurrentUplink];
- if (started && UplinkSock)
- {
- /* Send a quit for all of our bots */
- for (Anope::insensitive_map<BotInfo *>::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it)
- {
- BotInfo *bi = it->second;
-
- /* Don't use quitmsg here, it may contain information you don't want people to see */
- ircdproto->SendQuit(bi, "Shutting down");
- /* Erase bots from the user list so they don't get nuked later on */
- UserListByNick.erase(bi->nick);
- if (!bi->GetUID().empty())
- UserListByUID.erase(bi->GetUID());
- }
+ new UplinkSocket();
+ if (!Config->LocalHost.empty())
+ UplinkSock->Bind(Config->LocalHost);
+ FOREACH_MOD(I_OnPreServerConnect, OnPreServerConnect());
+ UplinkSock->Connect(u->host, u->port);
+}
- ircdproto->SendSquit(Config->ServerName, quitmsg);
+/*************************************************************************/
- for (Anope::insensitive_map<User *>::const_iterator it = UserListByNick.begin(); it != UserListByNick.end();)
- {
- User *u = it->second;
- ++it;
- delete u;
- }
- }
- delete UplinkSock;
- SocketEngine::Shutdown();
+void save_databases()
+{
+ if (readonly)
+ return;
- /* just in case they weren't all removed at least run once */
- ModuleManager::CleanupRuntimeDirectory();
+ EventReturn MOD_RESULT;
+ FOREACH_RESULT(I_OnSaveDatabase, OnSaveDatabase());
+ Log(LOG_DEBUG) << "Saving databases";
}
/*************************************************************************/
@@ -314,201 +313,132 @@ Anope::string GetFullProgDir(const Anope::string &argv0)
/*************************************************************************/
-static bool Connect()
-{
- /* Connect to the remote server */
- int servernum = 1;
- for (std::list<Uplink *>::iterator curr_uplink = Config->Uplinks.begin(), end_uplink = Config->Uplinks.end(); curr_uplink != end_uplink; ++curr_uplink, ++servernum)
- {
- uplink_server = *curr_uplink;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(I_OnPreServerConnect, OnPreServerConnect(*curr_uplink, servernum));
- if (MOD_RESULT != EVENT_CONTINUE)
- {
- if (MOD_RESULT == EVENT_STOP)
- continue;
- return true;
- }
-
- DNSRecord req = DNSManager::BlockingQuery(uplink_server->host, uplink_server->ipv6 ? DNS_QUERY_AAAA : DNS_QUERY_A);
-
- if (!req)
- {
- Log() << "Unable to connect to server " << servernum << " (" << uplink_server->host << ":" << uplink_server->port << "): Invalid hostname/IP";
- continue;
- }
-
- try
- {
- new UplinkSocket(uplink_server->ipv6);
- UplinkSock->Connect(req.result, uplink_server->port, Config->LocalHost);
- }
- catch (const SocketException &ex)
- {
- Log() << "Unable to connect to server " << servernum << " (" << uplink_server->host << ":" << uplink_server->port << "): " << ex.GetReason();
- continue;
- }
-
- Log() << "Connected to server " << servernum << " (" << uplink_server->host << ":" << uplink_server->port << ")";
- return true;
- }
-
- uplink_server = NULL;
-
- return false;
-}
-
-/*************************************************************************/
-
/* Main routine. (What does it look like? :-) ) */
int main(int ac, char **av, char **envp)
{
- try
- {
- my_av = av;
- my_envp = envp;
+ int return_code = 0;
- char cwd[PATH_MAX] = "";
+ char cwd[PATH_MAX] = "";
#ifdef _WIN32
- GetCurrentDirectory(PATH_MAX, cwd);
+ GetCurrentDirectory(PATH_MAX, cwd);
#else
- getcwd(cwd, PATH_MAX);
+ getcwd(cwd, PATH_MAX);
#endif
- orig_cwd = cwd;
+ orig_cwd = cwd;
#ifndef _WIN32
- /* If we're root, issue a warning now */
- if (!getuid() && !getgid())
- {
- fprintf(stderr, "WARNING: You are currently running Anope as the root superuser. Anope does not\n");
- fprintf(stderr, " require root privileges to run, and it is discouraged that you run Anope\n");
- fprintf(stderr, " as the root superuser.\n");
- }
+ /* If we're root, issue a warning now */
+ if (!getuid() && !getgid())
+ {
+ fprintf(stderr, "WARNING: You are currently running Anope as the root superuser. Anope does not\n");
+ fprintf(stderr, " require root privileges to run, and it is discouraged that you run Anope\n");
+ fprintf(stderr, " as the root superuser.\n");
+ }
#endif
- binary_dir = GetFullProgDir(av[0]);
- if (binary_dir[binary_dir.length() - 1] == '.')
- binary_dir = binary_dir.substr(0, binary_dir.length() - 2);
+ binary_dir = GetFullProgDir(av[0]);
+ if (binary_dir[binary_dir.length() - 1] == '.')
+ binary_dir = binary_dir.substr(0, binary_dir.length() - 2);
#ifdef _WIN32
- Anope::string::size_type n = binary_dir.rfind('\\');
- services_dir = binary_dir.substr(0, n) + "\\data";
+ Anope::string::size_type n = binary_dir.rfind('\\');
+ services_dir = binary_dir.substr(0, n) + "\\data";
#else
- Anope::string::size_type n = binary_dir.rfind('/');
- services_dir = binary_dir.substr(0, n) + "/data";
+ Anope::string::size_type n = binary_dir.rfind('/');
+ services_dir = binary_dir.substr(0, n) + "/data";
#endif
- /* Clean out the module runtime directory prior to running, just in case files were left behind during a previous run */
- ModuleManager::CleanupRuntimeDirectory();
+ /* Clean out the module runtime directory prior to running, just in case files were left behind during a previous run */
+ ModuleManager::CleanupRuntimeDirectory();
+ try
+ {
/* General initialization first */
Init(ac, av);
+ }
+ catch (const FatalException &ex)
+ {
+ Log() << ex.GetReason();
+ return -1;
+ }
- /* If the first connect fails give up, don't sit endlessly trying to reconnect */
- if (!Connect())
- {
- Log() << "Can't connect to any servers";
- return 0;
- }
+ try
+ {
+ Connect();
+ }
+ catch (const SocketException &ex)
+ {
+ Log() << ex.GetReason();
+ ModuleManager::UnloadAll();
+ SocketEngine::Shutdown();
+ for (Module *m; (m = ModuleManager::FindFirstOf(PROTOCOL)) != NULL;)
+ ModuleManager::UnloadModule(m, NULL);
+ ModuleManager::CleanupRuntimeDirectory();
+ return -1;
+ }
- ircdproto->SendConnect();
- FOREACH_MOD(I_OnServerConnect, OnServerConnect());
+ started = true;
- started = true;
+ /* Set up timers */
+ time_t last_check = Anope::CurTime;
+ UpdateTimer updateTimer(Config->UpdateTimeout);
- /* Set up timers */
- time_t last_check = Anope::CurTime;
- UpdateTimer updateTimer(Config->UpdateTimeout);
+ /*** Main loop. ***/
+ while (!quitting)
+ {
+ Log(LOG_DEBUG_2) << "Top of main loop";
- /*** Main loop. ***/
- while (!quitting)
+ if (Anope::CurTime - last_check >= Config->TimeoutCheck)
{
- while (!quitting && UplinkSock)
- {
- Log(LOG_DEBUG_2) << "Top of main loop";
-
- if (!readonly && (save_data || shutting_down))
- {
- if (shutting_down)
- ircdproto->SendGlobops(NULL, "Updating databases on shutdown, please wait.");
- save_databases();
- save_data = false;
- }
-
- if (shutting_down)
- {
- quitting = true;
- break;
- }
+ TimerManager::TickTimers(Anope::CurTime);
+ last_check = Anope::CurTime;
+ }
- if (Anope::CurTime - last_check >= Config->TimeoutCheck)
- {
- TimerManager::TickTimers(Anope::CurTime);
- last_check = Anope::CurTime;
- }
+ /* Free up any finished threads */
+ threadEngine.Process();
- /* Free up any finished threads */
- threadEngine.Process();
+ /* Process any modes that need to be (un)set */
+ if (Me->IsSynced())
+ ModeManager::ProcessModes();
- /* Process any modes that need to be (un)set */
- if (Me != NULL && Me->IsSynced())
- ModeManager::ProcessModes();
+ /* Process the socket engine */
+ SocketEngine::Process();
+ }
- /* Process the socket engine */
- SocketEngine::Process();
- }
+ if (save_data)
+ {
+ ircdproto->SendGlobops(NULL, "Updating databases on shutdown, please wait.");
+ save_databases();
+ save_data = false;
+ }
- if (quitting)
- /* Disconnect and exit */
- services_shutdown();
- else
- {
- FOREACH_MOD(I_OnServerDisconnect, OnServerDisconnect());
+ if (restarting)
+ {
+ FOREACH_MOD(I_OnRestart, OnRestart());
+ }
+ else
+ {
+ FOREACH_MOD(I_OnShutdown, OnShutdown());
+ }
- /* Clear all of our users, but not our bots */
- for (Anope::insensitive_map<User *>::const_iterator it = UserListByNick.begin(); it != UserListByNick.end();)
- {
- User *u = it->second;
- ++it;
+ if (quitmsg.empty())
+ quitmsg = "Terminating, reason unknown";
+ Log() << quitmsg;
- if (u->server != Me)
- delete u;
- }
+ ModuleManager::UnloadAll();
+ SocketEngine::Shutdown();
+ for (Module *m; (m = ModuleManager::FindFirstOf(PROTOCOL)) != NULL;)
+ ModuleManager::UnloadModule(m, NULL);
- Me->SetFlag(SERVER_SYNCING);
- for (unsigned i = Me->GetLinks().size(); i > 0; --i)
- if (!Me->GetLinks()[i - 1]->HasFlag(SERVER_JUPED))
- delete Me->GetLinks()[i - 1];
+ ModuleManager::CleanupRuntimeDirectory();
- unsigned j = 0;
- for (; j < (Config->MaxRetries ? Config->MaxRetries : j + 1); ++j)
- {
- Log() << "Disconnected from the server, retrying in " << Config->RetryWait << " seconds";
-
- sleep(Config->RetryWait);
- if (Connect())
- {
- ircdproto->SendConnect();
- FOREACH_MOD(I_OnServerConnect, OnServerConnect());
- break;
- }
- }
- if (Config->MaxRetries && j == Config->MaxRetries)
- {
- Log() << "Max connection retry limit exceeded";
- quitting = true;
- }
- }
- }
- }
- catch (const FatalException &ex)
+ if (restarting)
{
- if (!ex.GetReason().empty())
- Log(LOG_TERMINAL) << ex.GetReason();
- if (started)
- services_shutdown();
- return -1;
+ chdir(binary_dir.c_str());
+ av[0] = const_cast<char *>(("./" + services_bin).c_str());
+ execve(services_bin.c_str(), av, envp);
+ Log() << "Restart failed";
+ return_code = -1;
}
return 0;
diff --git a/src/messages.cpp b/src/messages.cpp
index f8d893eca..ed9e572ce 100644
--- a/src/messages.cpp
+++ b/src/messages.cpp
@@ -25,7 +25,7 @@ bool OnStats(const Anope::string &source, const std::vector<Anope::string> &para
if (u && u->HasMode(UMODE_OPER))
{
ircdproto->SendNumeric(Config->ServerName, 211, source, "Server SendBuf SentBytes SentMsgs RecvBuf RecvBytes RecvMsgs ConnTime");
- ircdproto->SendNumeric(Config->ServerName, 211, source, "%s %d %d %d %d %d %d %ld", uplink_server->host.c_str(), UplinkSock->WriteBufferLen(), TotalWritten, -1, UplinkSock->ReadBufferLen(), TotalRead, -1, static_cast<long>(Anope::CurTime - start_time));
+ ircdproto->SendNumeric(Config->ServerName, 211, source, "%s %d %d %d %d %d %d %ld", Config->Uplinks[CurrentUplink]->host.c_str(), UplinkSock->WriteBufferLen(), TotalWritten, -1, UplinkSock->ReadBufferLen(), TotalRead, -1, static_cast<long>(Anope::CurTime - start_time));
}
ircdproto->SendNumeric(Config->ServerName, 219, source, "%c :End of /STATS report.", params[0][0]);
diff --git a/src/misc.cpp b/src/misc.cpp
index fff224eed..966af359e 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -843,6 +843,15 @@ void Anope::Unhex(const Anope::string &src, char *dest)
dest[destpos] = 0;
}
+int Anope::LastErrorCode()
+{
+#ifndef _WIN32
+ return errno;
+#else
+ return GetLastError();
+#endif
+}
+
const Anope::string Anope::LastError()
{
#ifndef _WIN32
diff --git a/src/modulemanager.cpp b/src/modulemanager.cpp
index e2f837f0f..1c4e07eef 100644
--- a/src/modulemanager.cpp
+++ b/src/modulemanager.cpp
@@ -308,6 +308,8 @@ ModuleReturn ModuleManager::DeleteModule(Module *m)
ano_module_t handle = m->handle;
Anope::string filename = m->filename;
+ Log(LOG_DEBUG) << "Unloading module " << m->name;
+
ano_modclearerr();
void (*destroy_func)(Module *m) = function_cast<void (*)(Module *)>(dlsym(m->handle, "AnopeFini"));
const char *err = ano_moderr();
@@ -471,14 +473,14 @@ void ModuleManager::ClearCallBacks(Module *m)
delete m->CallBacks.front();
}
-/** Unloading all modules, NEVER call this when Anope isn't shutting down.
- * Ever.
+/** Unloading all modules except the protocol module.
*/
void ModuleManager::UnloadAll()
{
std::vector<Anope::string> modules[MT_END];
for (std::list<Module *>::iterator it = Modules.begin(), it_end = Modules.end(); it != it_end; ++it)
- modules[(*it)->type].push_back((*it)->name);
+ if ((*it)->type != PROTOCOL)
+ modules[(*it)->type].push_back((*it)->name);
for (size_t i = MT_BEGIN + 1; i != MT_END; ++i)
for (unsigned j = 0; j < modules[i].size(); ++j)
diff --git a/src/socketengines/socketengine_epoll.cpp b/src/socketengines/socketengine_epoll.cpp
index 0ced8e71e..316696f54 100644
--- a/src/socketengines/socketengine_epoll.cpp
+++ b/src/socketengines/socketengine_epoll.cpp
@@ -30,6 +30,8 @@ void SocketEngine::Init()
void SocketEngine::Shutdown()
{
+ Process();
+
for (std::map<int, Socket *>::const_iterator it = Sockets.begin(), it_end = Sockets.end(); it != it_end; ++it)
delete it->second;
Sockets.clear();
diff --git a/src/socketengines/socketengine_poll.cpp b/src/socketengines/socketengine_poll.cpp
index b8eef91b8..3202bbe29 100644
--- a/src/socketengines/socketengine_poll.cpp
+++ b/src/socketengines/socketengine_poll.cpp
@@ -37,6 +37,8 @@ void SocketEngine::Init()
void SocketEngine::Shutdown()
{
+ Process();
+
for (std::map<int, Socket *>::const_iterator it = Sockets.begin(), it_end = Sockets.end(); it != it_end; ++it)
delete it->second;
Sockets.clear();
diff --git a/src/socketengines/socketengine_select.cpp b/src/socketengines/socketengine_select.cpp
index 43b9b17cc..e23046b79 100644
--- a/src/socketengines/socketengine_select.cpp
+++ b/src/socketengines/socketengine_select.cpp
@@ -13,6 +13,8 @@ void SocketEngine::Init()
void SocketEngine::Shutdown()
{
+ Process();
+
for (std::map<int, Socket *>::const_iterator it = Sockets.begin(), it_end = Sockets.end(); it != it_end; ++it)
delete it->second;
Sockets.clear();
diff --git a/src/sockets.cpp b/src/sockets.cpp
index 96acbe981..c9758cb12 100644
--- a/src/sockets.cpp
+++ b/src/sockets.cpp
@@ -251,7 +251,7 @@ bool cidr::match(sockaddrs &other)
* @param sz How much to read
* @return Number of bytes received
*/
-int SocketIO::Recv(Socket *s, char *buf, size_t sz) const
+int SocketIO::Recv(Socket *s, char *buf, size_t sz)
{
size_t i = recv(s->GetFD(), buf, sz, 0);
TotalRead += i;
@@ -263,7 +263,7 @@ int SocketIO::Recv(Socket *s, char *buf, size_t sz) const
* @param buf What to write
* @return Number of bytes written
*/
-int SocketIO::Send(Socket *s, const Anope::string &buf) const
+int SocketIO::Send(Socket *s, const Anope::string &buf)
{
size_t i = send(s->GetFD(), buf.c_str(), buf.length(), 0);
TotalWritten += i;
@@ -272,8 +272,9 @@ int SocketIO::Send(Socket *s, const Anope::string &buf) const
/** Accept a connection from a socket
* @param s The socket
+ * @return The new client socket
*/
-void SocketIO::Accept(ListenSocket *s)
+ClientSocket *SocketIO::Accept(ListenSocket *s)
{
sockaddrs conaddr;
@@ -285,32 +286,44 @@ void SocketIO::Accept(ListenSocket *s)
#endif
if (newsock > 0 && newsock != INVALID_SOCKET)
- s->OnAccept(newsock, conaddr);
+ return s->OnAccept(newsock, conaddr);
else
throw SocketException("Unable to accept connection: " + Anope::LastError());
}
+/** Bind a socket
+ * @param s The socket
+ * @param ip The IP to bind to
+ * @param port The optional port to bind to
+ */
+void SocketIO::Bind(Socket *s, const Anope::string &ip, int port)
+{
+ s->bindaddr.pton(s->IsIPv6() ? AF_INET6 : AF_INET, ip, port);
+ if (bind(s->GetFD(), &s->bindaddr.sa, s->bindaddr.size()) == -1)
+ throw SocketException("Unable to bind to address: " + Anope::LastError());
+}
+
/** Connect the socket
* @param s THe socket
* @param target IP to connect to
* @param port to connect to
- * @param bindip IP to bind to, if any
*/
-void SocketIO::Connect(ConnectionSocket *s, const Anope::string &target, int port, const Anope::string &bindip)
+void SocketIO::Connect(ConnectionSocket *s, const Anope::string &target, int port)
{
- s->bindaddr.clear();
- s->conaddr.clear();
-
- if (!bindip.empty())
+ s->conaddr.pton(s->IsIPv6() ? AF_INET6 : AF_INET, target, port);
+ int c = connect(s->GetFD(), &s->conaddr.sa, s->conaddr.size());
+ if (c == -1)
{
- s->bindaddr.pton(s->IsIPv6() ? AF_INET6 : AF_INET, bindip, 0);
- if (bind(s->GetFD(), &s->bindaddr.sa, s->bindaddr.size()) == -1)
- throw SocketException(Anope::string("Unable to bind to address: ") + Anope::LastError());
+ if (Anope::LastErrorCode() != EINPROGRESS)
+ throw SocketException("Error connecting to server: " + Anope::LastError());
+ else
+ SocketEngine::MarkWritable(s);
+ }
+ else
+ {
+ s->OnConnect();
+ s->connected = true;
}
-
- s->conaddr.pton(s->IsIPv6() ? AF_INET6 : AF_INET, target, port);
- if (connect(s->GetFD(), &s->conaddr.sa, s->conaddr.size()) == -1 && errno != EINPROGRESS)
- throw SocketException(Anope::string("Error connecting to server: ") + Anope::LastError());
}
/** Empty constructor, used for things such as the pipe socket
@@ -335,6 +348,7 @@ Socket::Socket(int sock, bool ipv6, int type) : Flags<SocketFlag, 2>(SocketFlagS
this->Sock = socket(this->IPv6 ? AF_INET6 : AF_INET, type, 0);
else
this->Sock = sock;
+ this->SetNonBlocking();
SocketEngine::AddSocket(this);
}
@@ -391,6 +405,15 @@ bool Socket::SetNonBlocking()
#endif
}
+/** Bind the socket to an ip and port
+ * @param ip The ip
+ * @param port The port
+ */
+void Socket::Bind(const Anope::string &ip, int port)
+{
+ this->IO->Bind(this, ip, port);
+}
+
/** Called when there is something to be received for this socket
* @return true on success, false to drop this socket
*/
@@ -445,7 +468,9 @@ bool BufferedSocket::ProcessRead()
char tbuffer[NET_BUFSIZE] = "";
RecvLen = this->IO->Recv(this, tbuffer, sizeof(tbuffer) - 1);
- if (RecvLen <= 0)
+ if (RecvLen == -2)
+ return true;
+ else if (RecvLen <= 0)
return false;
Anope::string sbuffer = this->extrabuf;
@@ -482,10 +507,10 @@ bool BufferedSocket::ProcessRead()
*/
bool BufferedSocket::ProcessWrite()
{
- if (this->WriteBuffer.empty())
- return true;
int count = this->IO->Send(this, this->WriteBuffer);
- if (count <= -1)
+ if (count == -2)
+ return true;
+ else if (count <= -1)
return false;
this->WriteBuffer = this->WriteBuffer.substr(count);
if (this->WriteBuffer.empty())
@@ -557,10 +582,8 @@ ListenSocket::ListenSocket(const Anope::string &bindip, int port, bool ipv6) : S
this->Type = SOCKTYPE_LISTEN;
this->SetNonBlocking();
- this->listenaddrs.pton(IPv6 ? AF_INET6 : AF_INET, bindip, port);
-
- if (bind(Sock, &this->listenaddrs.sa, this->listenaddrs.size()) == -1)
- throw SocketException(Anope::string("Unable to bind to address: ") + Anope::LastError());
+ this->bindaddr.pton(IPv6 ? AF_INET6 : AF_INET, bindip, port);
+ this->IO->Bind(this, bindip, port);
if (listen(Sock, 5) == -1)
throw SocketException(Anope::string("Unable to listen: ") + Anope::LastError());
@@ -601,7 +624,7 @@ ClientSocket *ListenSocket::OnAccept(int fd, const sockaddrs &addr)
* @param ipv6 true to use IPv6
* @param type The socket type, defaults to SOCK_STREAM
*/
-ConnectionSocket::ConnectionSocket(bool ipv6, int type) : BufferedSocket(0, ipv6, type)
+ConnectionSocket::ConnectionSocket(bool ipv6, int type) : BufferedSocket(0, ipv6, type), connected(false)
{
this->Type = SOCKTYPE_CONNECTION;
}
@@ -609,19 +632,58 @@ ConnectionSocket::ConnectionSocket(bool ipv6, int type) : BufferedSocket(0, ipv6
/** Connect the socket
* @param TargetHost The target host to connect to
* @param Port The target port to connect to
- * @param BindHost The host to bind to for connecting
*/
-void ConnectionSocket::Connect(const Anope::string &TargetHost, int Port, const Anope::string &BindHost)
+void ConnectionSocket::Connect(const Anope::string &TargetHost, int Port)
{
- try
- {
- this->IO->Connect(this, TargetHost, Port, BindHost);
- }
- catch (const SocketException &)
+ this->IO->Connect(this, TargetHost, Port);
+}
+
+/** Called when the socket is ready to be written to
+ * @return true on success, false to drop this socket
+ */
+bool ConnectionSocket::ProcessWrite()
+{
+ if (!this->connected)
{
- delete this;
- throw;
+ SocketEngine::ClearWritable(this);
+
+ int optval = 0;
+ socklen_t optlen = sizeof(optval);
+ if (!getsockopt(this->GetFD(), SOL_SOCKET, SO_ERROR, &optval, &optlen) && !optval)
+ {
+ this->OnConnect();
+ this->connected = true;
+ return true;
+ }
+
+ errno = optval;
+ this->OnError(Anope::LastError());
+ return false;
}
+
+ return BufferedSocket::ProcessWrite();
+}
+
+/** Called when there is an error for this socket
+ * @return true on success, false to drop this socket
+ */
+void ConnectionSocket::ProcessError()
+{
+ if (!this->connected)
+ this->ProcessWrite();
+}
+
+/** Called on a successful connect
+ */
+void ConnectionSocket::OnConnect()
+{
+}
+
+/** Called when a connection is not successful
+ * @param error The error
+ */
+void ConnectionSocket::OnError(const Anope::string &error)
+{
}
/** Constructor