summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/inspircd.cpp183
1 files changed, 122 insertions, 61 deletions
diff --git a/src/inspircd.cpp b/src/inspircd.cpp
index b59a0c53c..feea64699 100644
--- a/src/inspircd.cpp
+++ b/src/inspircd.cpp
@@ -2434,6 +2434,16 @@ void ProcessUser(userrec* cu)
}
}
+/**
+ * This function is called once a second from the mainloop.
+ * It is intended to do background checking on all the user structs, e.g.
+ * stuff like ping checks, registration timeouts, etc.
+ * The function returns false when it is finished, and true if
+ * it needs to be run again (e.g. it has processed one of a batch of
+ * QUIT messages, but couldnt continue iterating because the iterator
+ * became invalid). This function is also responsible for checking
+ * if InspSocket derived classes are timed out.
+ */
bool DoBackgroundUserStuff(time_t TIME)
{
unsigned int numsockets = module_sockets.size();
@@ -2451,8 +2461,11 @@ bool DoBackgroundUserStuff(time_t TIME)
}
if (module_sockets.size() != numsockets) break;
}
+ /* TODO: We need a seperate hash containing only local users for this
+ */
for (user_hash::iterator count2 = clientlist.begin(); count2 != clientlist.end(); count2++)
{
+ /* Sanity checks for corrupted iterators (yes, really) */
userrec* curr = NULL;
if (count2->second)
curr = count2->second;
@@ -2677,14 +2690,22 @@ int InspIRCd(char** argv, int argc)
for (;;)
{
#ifdef _POSIX_PRIORITY_SCHEDULING
+ /* If we can, yield a bit. Doesnt do us any harm, if we're busy we take back some timeslice later ;-)
+ * it just means that if we have absolutely NOTHING to do, we dont eat up all the cpu doing nothing.
+ */
sched_yield(); sched_yield();
#endif
- // we only read time() once per iteration rather than tons of times!
+ /* time() seems to be a pretty expensive syscall, so avoid calling it too much.
+ * Once per loop iteration is pleanty.
+ */
OLDTIME = TIME;
TIME = time(NULL);
- // *FIX* Instead of closing sockets in kill_link when they receive the ERROR :blah line, we should queue
- // them in a list, then reap the list every second or so.
+ /* Run background module timers every few seconds
+ * (the docs say modules shouldnt rely on accurate
+ * timing using this event, so we dont have to
+ * time this exactly).
+ */
if (((TIME % 8) == 0) && (!expire_run))
{
expire_lines();
@@ -2695,84 +2716,124 @@ int InspIRCd(char** argv, int argc)
if ((TIME % 8) == 1)
expire_run = false;
+ /* Once a second, do the background processing */
if (TIME != OLDTIME)
while (DoBackgroundUserStuff(TIME));
+ /* Call the socket engine to wait on the active
+ * file descriptors. The socket engine has everything's
+ * descriptors in its list... dns, modules, users,
+ * servers... so its nice and easy, just one call.
+ */
SE->Wait(activefds);
+ /**
+ * Now process each of the fd's. For users, we have a fast
+ * lookup table which can find a user by file descriptor, so
+ * processing them by fd isnt expensive. If we have a lot of
+ * listening ports or module sockets though, things could get
+ * ugly.
+ */
unsigned int numberactive = activefds.size();
for (unsigned int activefd = 0; activefd < numberactive; activefd++)
{
int socket_type = SE->GetType(activefds[activefd]);
-
- if (socket_type == X_ESTAB_CLIENT)
+ switch (socket_type)
{
- log(DEBUG,"Got a ready socket of type X_ESTAB_CLIENT");
- userrec* cu = fd_ref_table[activefds[activefd]];
- if (cu)
- {
- /* It's a user */
- ProcessUser(cu);
- }
- }
- else if (socket_type == X_ESTAB_MODULE)
- {
- log(DEBUG,"Got a ready socket of type X_ESTAB_MODULE");
- unsigned int numsockets = module_sockets.size();
- for (std::vector<InspSocket*>::iterator a = module_sockets.begin(); a < module_sockets.end(); a++)
- {
- InspSocket* s = (InspSocket*)*a;
- if ((s) && (s->GetFd() == activefds[activefd]))
+ case X_ESTAB_CLIENT:
+
+ userrec* cu = fd_ref_table[activefds[activefd]];
+ if (cu)
+ ProcessUser(cu);
+
+ break;
+
+ case X_ESTAB_MODULE:
+
+ /* Process module-owned sockets.
+ * Modules are encouraged to inherit their sockets from
+ * InspSocket so we can process them neatly like this.
+ */
+ unsigned int numsockets = module_sockets.size();
+ for (std::vector<InspSocket*>::iterator a = module_sockets.begin(); a < module_sockets.end(); a++)
{
- if ((s->Timeout(TIME)) || (!s->Poll()))
+ InspSocket* s = (InspSocket*)*a;
+ if ((s) && (s->GetFd() == activefds[activefd]))
{
- log(DEBUG,"Socket poll returned false, close and bail");
- SE->DelFd(s->GetFd());
- s->Close();
- module_sockets.erase(a);
- delete s;
- break;
+ if (!s->Poll())
+ {
+ log(DEBUG,"Socket poll returned false, close and bail");
+ SE->DelFd(s->GetFd());
+ s->Close();
+ module_sockets.erase(a);
+ delete s;
+ break;
+ }
+ if (module_sockets.size() != numsockets) break;
}
- if (module_sockets.size() != numsockets) break;
}
- }
- }
- else if (socket_type == X_ESTAB_DNS)
- {
- log(DEBUG,"Got a ready socket of type X_ESTAB_DNS");
+
+ break;
+
+ case X_ESTAB_DNS:
+
+ /* When we are using single-threaded dns,
+ * the sockets for dns end up in our mainloop.
+ * When we are using multi-threaded dns,
+ * each thread has its own basic poll() loop
+ * within it, making them 'fire and forget'
+ * and independent of the mainloop.
+ */
+ log(DEBUG,"Got a ready socket of type X_ESTAB_DNS");
#ifndef THREADED_DNS
- dns_poll(activefds[activefd]);
+ dns_poll(activefds[activefd]);
#endif
- }
- else if (socket_type == X_LISTEN)
- {
- log(DEBUG,"Got a ready socket of type X_LISTEN");
- /* It maybe a listener */
- for (int count = 0; count < boundPortCount; count++)
- {
- if (activefds[activefd] == openSockfd[count])
+ break;
+
+ case X_LISTEN:
+
+ /* It's a listener */
+ for (int count = 0; count < boundPortCount; count++)
{
- char target[MAXBUF], resolved[MAXBUF];
- length = sizeof (client);
- incomingSockfd = accept (openSockfd[count], (struct sockaddr *) &client, &length);
- log(DEBUG,"Accepted socket %d",incomingSockfd);
- strlcpy (target, (char *) inet_ntoa (client.sin_addr), MAXBUF);
- strlcpy (resolved, target, MAXBUF);
- if (incomingSockfd >= 0)
- {
- FOREACH_MOD OnRawSocketAccept(incomingSockfd, resolved, ports[count]);
- statsAccept++;
- AddClient(incomingSockfd, resolved, ports[count], false, inet_ntoa (client.sin_addr));
- log(DEBUG,"Adding client on port %lu fd=%lu",(unsigned long)ports[count],(unsigned long)incomingSockfd);
- }
- else
+ if (activefds[activefd] == openSockfd[count])
{
- WriteOpers("*** WARNING: accept() failed on port %lu (%s)",(unsigned long)ports[count],target);
- log(DEBUG,"accept failed: %lu",(unsigned long)ports[count]);
- statsRefused++;
+ char target[MAXBUF];
+ length = sizeof (client);
+ incomingSockfd = accept (openSockfd[count], (struct sockaddr *) &client, &length);
+ log(DEBUG,"Accepted socket %d",incomingSockfd);
+ strlcpy (target, (char *) inet_ntoa (client.sin_addr), MAXBUF);
+ /* Years and years ago, we used to resolve here
+ * using gethostbyaddr(). That is sucky and we
+ * don't do that any more...
+ */
+ if (incomingSockfd >= 0)
+ {
+ FOREACH_MOD OnRawSocketAccept(incomingSockfd, target, ports[count]);
+ statsAccept++;
+ AddClient(incomingSockfd, target, ports[count], false, inet_ntoa (client.sin_addr));
+ log(DEBUG,"Adding client on port %lu fd=%lu",(unsigned long)ports[count],(unsigned long)incomingSockfd);
+ }
+ else
+ {
+ WriteOpers("*** WARNING: accept() failed on port %lu (%s)",(unsigned long)ports[count],target);
+ log(DEBUG,"accept failed: %lu",(unsigned long)ports[count]);
+ statsRefused++;
+ }
+ /* We've found out what port it belongs on,
+ * no need to iterate the rest
+ */
+ break;
}
}
- }
+ break;
+
+ default;
+ /* Something went wrong if we're in here.
+ * In fact, so wrong, im not quite sure
+ * what we would do, so for now, its going
+ * to safely do bugger all.
+ */
+ break;
}
}