]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/inspircd.cpp
Comment and tidyup
[user/henk/code/inspircd.git] / src / inspircd.cpp
index 00c7c5f79859229767cbb3b0b0c48af9faf4c826..feea64699b6e484f18a9533a609bc94f1c1ec989 100644 (file)
@@ -2402,7 +2402,14 @@ void ProcessUser(userrec* cu)
                                         // table, life is much easier (and FASTER)
                                         userrec* new_comp = fd_ref_table[currfd];
                                         if ((currfd < 0) || (!fd_ref_table[currfd]) || (old_comp != new_comp))
+                                       {
                                                 return;
+                                       }
+                                       else
+                                       {
+                                               /* The user is still here, flush their buffer */
+                                               current->FlushWriteBuf();
+                                       }
                                 }
                         }
                         return;
@@ -2427,15 +2434,43 @@ void ProcessUser(userrec* cu)
         }
 }
 
-void DoBackgroundUserStuff()
+/**
+ * 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();
+       for (std::vector<InspSocket*>::iterator a = module_sockets.begin(); a < module_sockets.end(); a++)
+       {
+               InspSocket* s = (InspSocket*)*a;
+               if (s->Timeout(TIME))
+               {
+                       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;
+       }
+       /* 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;
                 if ((long)curr == -1)
-                        return;
+                        return false;
 
                 if ((curr) && (curr->fd != 0))
                 {
@@ -2448,7 +2483,7 @@ void DoBackgroundUserStuff()
                                 {
                                         log(DEBUG,"InspIRCd: write error: %s",curr->GetWriteError().c_str());
                                         kill_link(curr,curr->GetWriteError().c_str());
-                                        return;
+                                        return true;
                                 }
                                 // registration timeout -- didnt send USER/NICK/HOST in the time specified in
                                 // their connection class.
@@ -2456,7 +2491,7 @@ void DoBackgroundUserStuff()
                                 {
                                         log(DEBUG,"InspIRCd: registration timeout: %s",curr->nick);
                                         kill_link(curr,"Registration timeout");
-                                        return;
+                                        return true;
                                 }
                                 if ((TIME > curr->signon) && (curr->registered == 3) && (AllModulesReportReady(curr)))
                                 {
@@ -2465,14 +2500,14 @@ void DoBackgroundUserStuff()
                                         statsDnsBad++;
                                         FullConnectUser(curr);
                                         if (fd_ref_table[currfd] != curr) // something changed, bail pronto
-                                               return;
+                                               return true;
                                  }
                                  if ((curr->dns_done) && (curr->registered == 3) && (AllModulesReportReady(curr)))
                                  {
                                        log(DEBUG,"dns done, registered=3, and modules ready, OK");
                                        FullConnectUser(curr);
                                        if (fd_ref_table[currfd] != curr) // something changed, bail pronto
-                                                return;
+                                                return true;
                                  }
                                  if ((TIME > curr->nping) && (isnick(curr->nick)) && (curr->registered == 7))
                                  {
@@ -2480,16 +2515,17 @@ void DoBackgroundUserStuff()
                                        {
                                               log(DEBUG,"InspIRCd: ping timeout: %s",curr->nick);
                                                kill_link(curr,"Ping timeout");
-                                               return;
+                                               return true;
                                        }
                                        Write(curr->fd,"PING :%s",ServerName);
                                        log(DEBUG,"InspIRCd: pinging: %s",curr->nick);
                                        curr->lastping = 0;
                                        curr->nping = TIME+curr->pingmax;       // was hard coded to 120
-                                 }
-                         }
-                 }
-         }
+                               }
+                       }
+               }
+       }
+       return false;
 }
 
 void OpenLog(char** argv, int argc)
@@ -2654,101 +2690,150 @@ 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.
-               if (((TIME % 5) == 0) && (!expire_run))
+               /* 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();
                        FOREACH_MOD OnBackgroundTimer(TIME);
                        expire_run = true;
                        continue;
                }
-               if ((TIME % 5) == 1)
+               if ((TIME % 8) == 1)
                        expire_run = false;
                
-               DoBackgroundUserStuff();
-
+               /* 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;
                        }
                }