summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordanieldg <danieldg@e03df62e-2008-0410-955e-edbf42e46eb7>2009-09-27 00:22:29 +0000
committerdanieldg <danieldg@e03df62e-2008-0410-955e-edbf42e46eb7>2009-09-27 00:22:29 +0000
commit934d9a6a184b7a8600fcda30e012ba6f29f17b64 (patch)
tree44edb8dc4551777fcbdb73ef1a28e9b9d44bd9a6
parent7c1352df0c8bb2624d4f2cc8320467578c39a6ad (diff)
SendQ bugfixes
Fix DoWrite running on errored sockets Add testnet module for sendq and shutdown testing Prevent DoWrite from trying to write when writes are blocking git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@11768 e03df62e-2008-0410-955e-edbf42e46eb7
-rw-r--r--conf/modules.conf.example6
-rw-r--r--src/inspsocket.cpp37
-rw-r--r--src/modules/m_testnet.cpp66
-rw-r--r--src/users.cpp9
4 files changed, 98 insertions, 20 deletions
diff --git a/conf/modules.conf.example b/conf/modules.conf.example
index d5b4053f0..d3e77ca26 100644
--- a/conf/modules.conf.example
+++ b/conf/modules.conf.example
@@ -1685,6 +1685,12 @@
#<module name="m_swhois.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Test module: enable this to create a command useful in testing
+# flood control. To avoid accidental use on live networks, the server
+# name must contain ".test" to load the module
+#<module name="m_testnet.so">
+
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Timed bans module: Adds timed channel bans and the /TBAN command
#<module name="m_timedbans.so">
diff --git a/src/inspsocket.cpp b/src/inspsocket.cpp
index a46907ca5..9acd484fd 100644
--- a/src/inspsocket.cpp
+++ b/src/inspsocket.cpp
@@ -215,6 +215,11 @@ void StreamSocket::DoWrite()
{
if (sendq.empty())
return;
+ if (!error.empty() || fd < 0 || fd == INT_MAX)
+ {
+ ServerInstance->Logs->Log("SOCKET", DEBUG, "DoWrite on errored or closed socket");
+ return;
+ }
if (IOHook)
{
@@ -272,11 +277,13 @@ void StreamSocket::DoWrite()
}
else
{
- bool again = true;
- while (again)
+ // don't even try if we are known to be blocking
+ if (GetEventMask() & FD_WRITE_WILL_BLOCK)
+ return;
+ // start out optimistic - we won't need to write any more
+ int eventChange = FD_WANT_EDGE_WRITE;
+ while (sendq_len && eventChange == FD_WANT_EDGE_WRITE)
{
- again = false;
-
// Prepare a writev() call to write all buffers efficiently
int bufcount = sendq.size();
@@ -284,14 +291,15 @@ void StreamSocket::DoWrite()
if (bufcount > IOV_MAX)
{
bufcount = IOV_MAX;
- again = true;
}
+ int rv_max = 0;
iovec* iovecs = new iovec[bufcount];
for(int i=0; i < bufcount; i++)
{
iovecs[i].iov_base = const_cast<char*>(sendq[i].data());
iovecs[i].iov_len = sendq[i].length();
+ rv_max += sendq[i].length();
}
int rv = writev(fd, iovecs, bufcount);
delete[] iovecs;
@@ -310,7 +318,7 @@ void StreamSocket::DoWrite()
while (rv > 0 && !sendq.empty())
{
std::string& front = sendq.front();
- if (front.length() < (size_t)rv)
+ if (front.length() <= (size_t)rv)
{
// this string got fully written out
rv -= front.length();
@@ -323,6 +331,11 @@ void StreamSocket::DoWrite()
rv = 0;
}
}
+ if (rv < rv_max)
+ {
+ // it's going to block now
+ eventChange = FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK;
+ }
}
else if (rv == 0)
{
@@ -330,11 +343,11 @@ void StreamSocket::DoWrite()
}
else if (errno == EAGAIN)
{
- again = false;
+ eventChange = FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK;
}
else if (errno == EINTR)
{
- again = true;
+ // restart interrupted syscall
}
else
{
@@ -346,15 +359,9 @@ void StreamSocket::DoWrite()
// error - kill all events
ServerInstance->SE->ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
}
- else if (sendq_len)
- {
- // writes have blocked, we can use FAST_WRITE to find when they unblock
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK);
- }
else
{
- // writes are done, we can use EDGE_WRITE to stop asking for write
- ServerInstance->SE->ChangeEventMask(this, FD_WANT_EDGE_WRITE);
+ ServerInstance->SE->ChangeEventMask(this, eventChange);
}
}
}
diff --git a/src/modules/m_testnet.cpp b/src/modules/m_testnet.cpp
new file mode 100644
index 000000000..a4bdfc561
--- /dev/null
+++ b/src/modules/m_testnet.cpp
@@ -0,0 +1,66 @@
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2009 InspIRCd Development Team
+ * See: http://wiki.inspircd.org/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/* $ModDesc: Provides a module for testing the server while linked in a network */
+
+#include "inspircd.h"
+
+class CommandTest : public Command
+{
+ public:
+ CommandTest(Module* parent) : Command(parent, "TEST", 1)
+ {
+ syntax = "<action> <parameters>";
+ }
+
+ CmdResult Handle(const std::vector<std::string> &parameters, User *user)
+ {
+ if (parameters[0] == "flood")
+ {
+ unsigned int count = parameters.size() > 1 ? atoi(parameters[1].c_str()) : 100;
+ std::string line = parameters.size() > 2 ? parameters[2] : ":z.z NOTICE !flood :Flood text";
+ for(unsigned int i=0; i < count; i++)
+ user->Write(line);
+ }
+ else if (parameters[0] == "freeze")
+ {
+ user->Penalty += 100;
+ }
+ else if (parameters[0] == "shutdown")
+ {
+ int i = parameters.size() > 1 ? atoi(parameters[1].c_str()) : 2;
+ ServerInstance->SE->Shutdown(user->GetFd(), i);
+ }
+ return CMD_SUCCESS;
+ }
+};
+
+class ModuleTest : public Module
+{
+ CommandTest cmd;
+ public:
+ ModuleTest() : cmd(this)
+ {
+ if (!strstr(ServerInstance->Config->ServerName, ".test"))
+ throw ModuleException("Don't load modules without reading their descriptions!");
+ ServerInstance->AddCommand(&cmd);
+ }
+
+ Version GetVersion()
+ {
+ return Version("Provides a module for testing the server while linked in a network", VF_VENDOR|VF_OPTCOMMON);
+ }
+};
+
+MODULE_INIT(ModuleTest)
+
diff --git a/src/users.cpp b/src/users.cpp
index 1f38dfada..fb36ac324 100644
--- a/src/users.cpp
+++ b/src/users.cpp
@@ -575,13 +575,12 @@ void User::AddWriteBuf(const std::string &data)
if (!quitting && MyClass && getSendQSize() + data.length() > MyClass->GetSendqHardMax() && !HasPrivPermission("users/flood/increased-buffers"))
{
/*
- * Fix by brain - Set the error text BEFORE calling, because
- * if we dont it'll recursively call here over and over again trying
- * to repeatedly add the text to the sendq!
+ * Quit the user FIRST, because otherwise we could recurse
+ * here and hit the same limit.
*/
ServerInstance->Users->QuitUser(this, "SendQ exceeded");
- ServerInstance->SNO->WriteToSnoMask('a', "User %s SendQ of %lu exceeds connect class maximum of %lu",
- nick.c_str(), (unsigned long)getSendQSize() + data.length(), MyClass->GetSendqHardMax());
+ ServerInstance->SNO->WriteToSnoMask('a', "User %s SendQ exceeds connect class maximum of %lu",
+ nick.c_str(), MyClass->GetSendqHardMax());
return;
}