]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/inspircd.cpp
Dns poll not called often enough
[user/henk/code/inspircd.git] / src / inspircd.cpp
index f82be162e367f3b12f2dc62b26ec0a5da57f1736..21e3bffd97759d51492b86151b46a49208cc9af3 100644 (file)
@@ -109,10 +109,10 @@ int openSockfd[MAXSOCKS];
 bool nofork = false;
 bool unlimitcore = false;
 
-time_t TIME = time(NULL);
+time_t TIME = time(NULL), OLDTIME = time(NULL);
 
 #ifdef USE_KQUEUE
-int kq;
+int kq, lkq, skq;
 #endif
 
 namespace nspace
@@ -2397,16 +2397,6 @@ int main(int argc, char** argv)
        lowermap[']'] = '}';
        lowermap['\\'] = '|';
 
-#ifdef USE_KQUEUE
-       kq = kqueue();
-       if (kq == -1)
-       {
-               log(DEFAULT,"main: kqueue() failed!");
-               printf("ERROR: could not initialise kqueue event system. Shutting down.\n");
-               Exit(ERROR);
-       }
-#endif
-
        if (InspIRCd(argv,argc) == ERROR)
        {
                log(DEFAULT,"main: daemon function bailed");
@@ -2614,10 +2604,41 @@ void AddClient(int socket, char* host, int port, bool iscached, char* ip)
 
 #ifdef USE_KQUEUE
        struct kevent ke;
+       log(DEBUG,"kqueue: Add user to events, kq=%d socket=%d",kq,socket);
        EV_SET(&ke, socket, EVFILT_READ, EV_ADD, 0, 0, NULL);
         int i = kevent(kq, &ke, 1, 0, 0, NULL);
         if (i == -1)
         {
+               switch (errno)
+               {
+                       case EACCES:
+                               log(DEBUG,"kqueue: EACCES");
+                       break;
+                       case EFAULT:
+                               log(DEBUG,"kqueue: EFAULT");
+                       break;
+                       case EBADF:
+                               log(DEBUG,"kqueue: EBADF=%d",ke.ident);
+                       break;
+                       case EINTR:
+                               log(DEBUG,"kqueue: EINTR");
+                       break;
+                       case EINVAL:
+                               log(DEBUG,"kqueue: EINVAL");
+                       break;
+                       case ENOENT:
+                               log(DEBUG,"kqueue: ENOENT");
+                       break;
+                       case ENOMEM:
+                               log(DEBUG,"kqueue: ENOMEM");
+                       break;
+                       case ESRCH:
+                               log(DEBUG,"kqueue: ESRCH");
+                       break;
+                       default:
+                               log(DEBUG,"kqueue: UNKNOWN!");
+                       break;
+               }
                 log(DEBUG,"kqueue: Failed to add user to queue!");
         }
 
@@ -2874,7 +2895,12 @@ std::string GetVersionString()
         s1 = savept;
         v2 = strtok_r(s1," ",&savept);
         s1 = savept;
-       snprintf(versiondata,MAXBUF,"%s Rev. %s %s :%s (O=%lu)",VERSION,v2,ServerName,SYSTEM,(unsigned long)OPTIMISATION);
+#ifdef USE_KQUEUE
+       char socketengine[] = "kqueue";
+#else
+       char socketengine[] = "select";
+#endif
+       snprintf(versiondata,MAXBUF,"%s Rev. %s %s :%s (O=%lu) [SE=%s]",VERSION,v2,ServerName,SYSTEM,(unsigned long)OPTIMISATION,socketengine);
        return versiondata;
 }
 
@@ -4043,7 +4069,9 @@ int InspIRCd(char** argv, int argc)
        WritePID(PID);
          
        /* setup select call */
+#ifndef USE_KQUEUE
        FD_ZERO(&selectFds);
+#endif
        log(DEBUG,"InspIRCd: startup: zero selects");
        log(VERBOSE,"InspIRCd: startup: portCount = %lu", (unsigned long)portCount);
        
@@ -4091,11 +4119,65 @@ int InspIRCd(char** argv, int argc)
                 }
         }
 
+       // BUGFIX: We cannot initialize this before forking, as the kqueue data is not inherited by child processes!
+#ifdef USE_KQUEUE
+        kq = kqueue();
+       lkq = kqueue();
+       skq = kqueue();
+        if ((kq == -1) || (lkq == -1) || (skq == -1))
+        {
+                log(DEFAULT,"main: kqueue() failed!");
+                printf("ERROR: could not initialise kqueue event system. Shutting down.\n");
+                Exit(ERROR);
+        }
+#endif
+
+
+#ifdef USE_KQUEUE
+       log(DEFAULT,"kqueue socket engine is enabled. Filling listen list.");
+       for (count = 0; count < boundPortCount; count++)
+       {
+               struct kevent ke;
+               log(DEBUG,"kqueue: Add listening socket to events, kq=%d socket=%d",lkq,openSockfd[count]);
+               EV_SET(&ke, openSockfd[count], EVFILT_READ, EV_ADD, 0, 5, NULL);
+               int i = kevent(lkq, &ke, 1, 0, 0, NULL);
+               if (i == -1)
+               {
+                       log(DEFAULT,"main: add listen ports to kqueue failed!");
+                       printf("ERROR: could not initialise listening sockets in kqueue. Shutting down.\n");
+               }
+       }
+        for (int t = 0; t != SERVERportCount; t++)
+        {
+                struct kevent ke;
+                if (me[t])
+                {
+                       log(DEBUG,"kqueue: Add listening SERVER socket to events, kq=%d socket=%d",skq,me[t]->fd);
+                       EV_SET(&ke, me[t]->fd, EVFILT_READ, EV_ADD, 0, 5, NULL);
+                       int i = kevent(skq, &ke, 1, 0, 0, NULL);
+                       if (i == -1)
+                       {
+                               log(DEFAULT,"main: add server listen ports to kqueue failed!");
+                               printf("ERROR: could not initialise listening server sockets in kqueue. Shutting down.\n");
+                       }
+               }
+        }
+
+
+#else
+       log(DEFAULT,"Using standard select socket engine.");
+#endif
+
        WritePID(PID);
 
        length = sizeof (client);
        char tcp_msg[MAXBUF],tcp_host[MAXBUF];
 
+#ifdef USE_KQUEUE
+        struct kevent ke;
+       struct kevent ke_list[33];
+        struct timespec ts;
+#endif
         fd_set serverfds;
         timeval tvs;
         tvs.tv_usec = 10000L;
@@ -4108,7 +4190,7 @@ int InspIRCd(char** argv, int argc)
         tval.tv_usec = 10000L;
         tval.tv_sec = 0;
         int total_in_this_set = 0;
-       int v = 0;
+       int i = 0, v = 0, j = 0, cycle_iter = 0;
        bool expire_run = false;
          
        /* main loop, this never returns */
@@ -4117,13 +4199,16 @@ int InspIRCd(char** argv, int argc)
 #ifdef _POSIX_PRIORITY_SCHEDULING
                sched_yield();
 #endif
-                // poll dns queue
-                dns_poll();
+#ifndef USE_KQUEUE
                FD_ZERO(&sfd);
+#endif
 
                // we only read time() once per iteration rather than tons of times!
+               OLDTIME = TIME;
                TIME = time(NULL);
 
+               dns_poll();
+
                // *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))
@@ -4139,16 +4224,25 @@ int InspIRCd(char** argv, int argc)
                // fix by brain - this must be below any manipulation of the hashmap by modules
                user_hash::iterator count2 = clientlist.begin();
 
+#ifdef USE_KQUEUE
+               ts.tv_sec = 0;
+               ts.tv_nsec = 30000L;
+               i = kevent(skq, NULL, 0, &ke, 1, &ts);
+               if (i > 0)
+               {
+                       log(DEBUG,"kqueue: Listening server socket event, i=%d, ke.ident=%d",i,ke.ident);
+                       for (int x = 0; x != SERVERportCount; x++)
+                       {
+                               if ((me[x]) && (ke.ident == me[x]->fd))
+                               {
+
+#else
                FD_ZERO(&serverfds);
-               
                for (int x = 0; x != SERVERportCount; x++)
                {
                        if (me[x])
                                FD_SET(me[x]->fd, &serverfds);
                }
-               
-               // serverFds timevals went here
-               
                tvs.tv_usec = 30000L;
                tvs.tv_sec = 0;
                int servresult = select(32767, &serverfds, NULL, NULL, &tvs);
@@ -4158,6 +4252,7 @@ int InspIRCd(char** argv, int argc)
                        {
                                if ((me[x]) && (FD_ISSET (me[x]->fd, &serverfds)))
                                {
+#endif
                                        char remotehost[MAXBUF],resolved[MAXBUF];
                                        length = sizeof (client);
                                        incomingSockfd = accept (me[x]->fd, (sockaddr *) &client, &length);
@@ -4215,7 +4310,9 @@ int InspIRCd(char** argv, int argc)
        
        while (count2 != clientlist.end())
        {
+#ifndef USE_KQUEUE
                FD_ZERO(&sfd);
+#endif
 
                total_in_this_set = 0;
 
@@ -4294,11 +4391,12 @@ int InspIRCd(char** argv, int argc)
                        endingiter = count2;
                                count2 = xcount; // roll back to where we were
 #else
-                       dns_poll();
                        // KQUEUE: We don't go through a loop to fill the fd_set so instead we must manually do this loop every now and again.
                        // TODO: We dont need to do all this EVERY loop iteration, tone down the visits to this if we're using kqueue.
-                       while (count2 != clientlist.end())
+                       cycle_iter++;
+                       if (cycle_iter > 10) while (count2 != clientlist.end())
                        {
+                               cycle_iter = 0;
                                if (count2 != clientlist.end())
                                {
                                        curr = count2->second;
@@ -4351,9 +4449,6 @@ int InspIRCd(char** argv, int argc)
                        v = 0;
 
 #ifdef USE_KQUEUE
-                       struct kevent ke;
-                       int fd_to_process = 0;
-                       struct timespec ts;
                        ts.tv_sec = 0;
                        ts.tv_nsec = 1000L;
                        // for now, we only read 1 event. We could read soooo many more :)
@@ -4524,14 +4619,18 @@ int InspIRCd(char** argv, int argc)
                                else
                                if (result == 0)
                                {
+#ifndef USE_KQUEUE
                                        if (count2->second)
                                        {
+#endif
                                                log(DEBUG,"InspIRCd: Exited: %s",cu->nick);
                                                kill_link(cu,"Client exited");
                                                // must bail here? kill_link removes the hash, corrupting the iterator
                                                log(DEBUG,"Bailing from client exit");
                                                goto label;
+#ifndef USE_KQUEUE
                                        }
+#endif
                                }
                                else if (result > 0)
                                {
@@ -4548,6 +4647,7 @@ int InspIRCd(char** argv, int argc)
         sched_yield();
 #endif
        
+#ifndef USE_KQUEUE
        // set up select call
        for (count = 0; count < boundPortCount; count++)
        {
@@ -4560,11 +4660,27 @@ int InspIRCd(char** argv, int argc)
        /* select is reporting a waiting socket. Poll them all to find out which */
        if (selectResult > 0)
        {
-               char target[MAXBUF], resolved[MAXBUF];
-               for (count = 0; count < boundPortCount; count++)                
+               for (count = 0; count < boundPortCount; count++)
                {
                        if (FD_ISSET (openSockfd[count], &selectFds))
                        {
+#else
+       ts.tv_sec = 0;
+       ts.tv_nsec = 30000L;
+       i = kevent(lkq, NULL, 0, ke_list, 32, &ts);
+       if (i > 0) for (j = 0; j < i; j++)
+       {
+               log(DEBUG,"kqueue: Listening socket event, i=%d, ke.ident=%d",i,ke.ident);
+               // this isnt as efficient as it could be, we could create a reference table
+               // to reference bound ports by fd, but this isnt a big bottleneck as the actual
+               // number of listening ports on the average ircd is a small number (less than 20)
+               // compared to the number of clients (possibly over 2000)
+               for (count = 0; count < boundPortCount; count++)
+               {
+                       if (ke_list[j].ident == openSockfd[count])
+                       {
+#endif
+                               char target[MAXBUF], resolved[MAXBUF];
                                length = sizeof (client);
                                incomingSockfd = accept (openSockfd[count], (struct sockaddr *) &client, &length);
                              
@@ -4584,7 +4700,7 @@ int InspIRCd(char** argv, int argc)
                                        AddClient(incomingSockfd, resolved, ports[count], false, inet_ntoa (client.sin_addr));
                                        log(DEBUG,"InspIRCd: adding client on port %lu fd=%lu",(unsigned long)ports[count],(unsigned long)incomingSockfd);
                                }
-                               goto label;
+                               //goto label;
                        }
                }
        }