#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
+#ifdef USE_KQUEUE
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#endif
#include <cstdio>
#include <time.h>
#include <string>
bool nofork = false;
bool unlimitcore = false;
-time_t TIME = time(NULL);
+time_t TIME = time(NULL), OLDTIME = time(NULL);
+
+#ifdef USE_KQUEUE
+int kq, lkq, skq;
+#endif
namespace nspace
{
}
snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name);
- for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
+
+ std::vector<char*> *ulist = c->GetUsers();
+ for (int i = 0; i < ulist->size(); i++)
{
- if (has_channel(i->second,c))
+ char* o = (*ulist)[i];
+ userrec* otheruser = (userrec*)o;
+ if ((!has_channel(user,c)) && (strchr(otheruser->modes,'i')))
{
- if (isnick(i->second->nick))
- {
- if ((!has_channel(i->second,c)) && (strchr(i->second->modes,'i')))
- {
- /* user is +i, and source not on the channel, does not show
- * nick in NAMES list */
- continue;
- }
- strlcat(list,cmode(i->second,c),MAXBUF);
- strlcat(list,i->second->nick,MAXBUF);
- strlcat(list," ",MAXBUF);
- if (strlen(list)>(480-NICKMAX))
- {
- /* list overflowed into
- * multiple numerics */
- WriteServ(user->fd,"%s",list);
- snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name);
- }
- }
+ /* user is +i, and source not on the channel, does not show
+ * nick in NAMES list */
+ continue;
+ }
+ strlcat(list,cmode(otheruser,c),MAXBUF);
+ strlcat(list,otheruser->nick,MAXBUF);
+ strlcat(list," ",MAXBUF);
+ if (strlen(list)>(480-NICKMAX))
+ {
+ /* list overflowed into
+ * multiple numerics */
+ WriteServ(user->fd,"%s",list);
+ snprintf(list,MAXBUF,"353 %s = %s :", user->nick, c->name);
}
}
- /* if whats left in the list isnt empty, send it */ if (list[strlen(list)-1] != ':')
+ /* if whats left in the list isnt empty, send it */
+ if (list[strlen(list)-1] != ':')
{
WriteServ(user->fd,"%s",list);
}
if (user->fd > -1)
{
FOREACH_MOD OnRawSocketClose(user->fd);
- shutdown(user->fd,2);
- close(user->fd);
+#ifdef USE_KQUEUE
+ struct kevent ke;
+ EV_SET(&ke, user->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
+ int i = kevent(kq, &ke, 1, 0, 0, NULL);
+ if (i == -1)
+ {
+ log(DEBUG,"kqueue: Failed to remove user from queue!");
+ }
+#endif
+ shutdown(user->fd,2);
+ close(user->fd);
}
if (user->registered == 7) {
if (user->fd > -1)
{
FOREACH_MOD OnRawSocketClose(user->fd);
+#ifdef USE_KQUEUE
+ struct kevent ke;
+ EV_SET(&ke, user->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
+ int i = kevent(kq, &ke, 1, 0, 0, NULL);
+ if (i == -1)
+ {
+ log(DEBUG,"kqueue: Failed to remove user from queue!");
+ }
+#endif
shutdown(user->fd,2);
close(user->fd);
}
}
}
fd_ref_table[socket] = clientlist[tempnick];
+
+#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!");
+ }
+
+#endif
}
// this function counts all users connected, wether they are registered or NOT.
void ShowMOTD(userrec *user)
{
+ char buf[65536];
std::string WholeMOTD = "";
if (!MOTD.size())
{
WriteServ(user->fd,"422 %s :Message of the day file is missing.",user->nick);
return;
}
- WholeMOTD = std::string(":") + std::string(ServerName) + std::string(" 375 ") + std::string(user->nick) + std::string(" :- ") + std::string(ServerName) + " message of the day\r\n";
+ snprintf(buf,65535,":%s 375 %s :- %s message of the day\r\n", ServerName, user->nick, ServerName);
+ WholeMOTD = WholeMOTD + buf;
for (int i = 0; i != MOTD.size(); i++)
{
- WholeMOTD = WholeMOTD + std::string(":") + std::string(ServerName) + std::string(" 372 ") + std::string(user->nick) + std::string(" :- ") + MOTD[i] + std::string("\r\n");
+ snprintf(buf,65535,":%s 372 %s :- %s\r\n", ServerName, user->nick, MOTD[i].c_str());
+ WholeMOTD = WholeMOTD + buf;
}
- WholeMOTD = WholeMOTD + std::string(":") + std::string(ServerName) + std::string(" 376 ") + std::string(user->nick) + std::string(" :End of message of the day.\r\n");
+ snprintf(buf,65535,":%s 376 %s :End of message of the day.\r\n", ServerName, user->nick);
+ WholeMOTD = WholeMOTD + buf;
// only one write operation
send(user->fd,WholeMOTD.c_str(),WholeMOTD.length(),0);
statsSent += WholeMOTD.length();
v << "MESHED WALLCHOPS MODES=13 CHANTYPES=# PREFIX=(ohv)@%+ MAP SAFELIST MAXCHANNELS=" << MAXCHANS;
v << " MAXBANS=60 NICKLEN=" << NICKMAX;
v << " TOPICLEN=307 KICKLEN=307 MAXTARGETS=20 AWAYLEN=307 CHANMODES=ohvb,k,l,psmnti NETWORK=";
- v << std::string(Network);
+ v << Network;
std::string data005 = v.str();
FOREACH_MOD On005Numeric(data005);
// anfl @ #ratbox, efnet reminded me that according to the RFC this cant contain more than 13 tokens per line...
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;
}
bool UnloadModule(const char* filename)
{
+ std::string filename_str = filename;
for (int j = 0; j != module_names.size(); j++)
{
- if (module_names[j] == std::string(filename))
+ if (module_names[j] == filename_str)
{
if (modules[j]->GetVersion().Flags & VF_STATIC)
{
{
char modfile[MAXBUF];
snprintf(modfile,MAXBUF,"%s/%s",ModPath,filename);
+ std::string filename_str = filename;
if (!DirValid(modfile))
{
log(DEFAULT,"Module %s is not within the modules directory.",modfile);
{
for (int j = 0; j < module_names.size(); j++)
{
- if (module_names[j] == std::string(filename))
+ if (module_names[j] == filename_str)
{
log(DEFAULT,"Module %s is already loaded, cannot load a module twice!",modfile);
snprintf(MODERR,MAXBUF,"Module already loaded");
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);
}
}
+ // 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;
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 */
#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))
// 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);
{
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);
}
}
-
while (count2 != clientlist.end())
{
+#ifndef USE_KQUEUE
FD_ZERO(&sfd);
+#endif
+
total_in_this_set = 0;
user_hash::iterator xcount = count2;
//
// This should be up to 64x faster than the
// old implementation.
- while (total_in_this_set < 64)
+#ifndef USE_KQUEUE
+ while (total_in_this_set < 1024)
{
if (count2 != clientlist.end())
{
}
else break;
}
-
endingiter = count2;
count2 = xcount; // roll back to where we were
+#else
+ // 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.
+ cycle_iter++;
+ if (cycle_iter > 10) while (count2 != clientlist.end())
+ {
+ cycle_iter = 0;
+ if (count2 != clientlist.end())
+ {
+ curr = count2->second;
+ // we don't check the state of remote users.
+ if ((curr->fd != -1) && (curr->fd != FD_MAGIC_NUMBER))
+ {
+ // registration timeout -- didnt send USER/NICK/HOST in the time specified in
+ // their connection class.
+ if ((TIME > curr->timeout) && (curr->registered != 7))
+ {
+ log(DEBUG,"InspIRCd: registration timeout: %s",curr->nick);
+ kill_link(curr,"Registration timeout");
+ goto label;
+ }
+ if ((TIME > curr->signon) && (curr->registered == 3) && (AllModulesReportReady(curr)))
+ {
+ log(DEBUG,"signon exceed, registered=3, and modules ready, OK: %d %d",TIME,curr->signon);
+ curr->dns_done = true;
+ statsDnsBad++;
+ FullConnectUser(curr);
+ goto label;
+ }
+ if ((curr->dns_done) && (curr->registered == 3) && (AllModulesReportReady(curr)))
+ {
+ log(DEBUG,"dns done, registered=3, and modules ready, OK");
+ FullConnectUser(curr);
+ goto label;
+ }
+ if ((TIME > curr->nping) && (isnick(curr->nick)) && (curr->registered == 7))
+ {
+ if ((!curr->lastping) && (curr->registered == 7))
+ {
+ log(DEBUG,"InspIRCd: ping timeout: %s",curr->nick);
+ kill_link(curr,"Ping timeout");
+ goto label;
+ }
+ 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
+ }
+ }
+ }
+ else break;
+ count2++;
+ }
+ // increment the counter right to the end of the list, as kqueue processes everything in one go
+#endif
v = 0;
- // tvals defined here
-
+#ifdef USE_KQUEUE
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000L;
+ // for now, we only read 1 event. We could read soooo many more :)
+ int i = kevent(kq, NULL, 0, &ke, 1, &ts);
+ if (i > 0)
+ {
+ log(DEBUG,"kevent call: kq=%d, i=%d",kq,i);
+ // KQUEUE: kevent gives us ONE fd which is ready to have something done to it. Do something to it.
+ userrec* cu = fd_ref_table[ke.ident];
+#else
tval.tv_usec = 1000L;
selectResult2 = select(65535, &sfd, NULL, NULL, &tval);
if (selectResult2 > 0)
for (user_hash::iterator count2a = xcount; count2a != endingiter; count2a++)
{
+ // SELECT: we have to iterate...
+ userrec* cu = count2a->second;
+#endif
#ifdef _POSIX_PRIORITY_SCHEDULING
sched_yield();
#endif
- userrec* cu = count2a->second;
result = EAGAIN;
+#ifdef USE_KQUEUE
+ // KQUEUE: We already know we have a valid FD. No checks needed.
+ if ((cu->fd != FD_MAGIC_NUMBER) && (cu->fd != -1))
+#else
+ // SELECT: We don't know if our FD is valid.
if ((cu->fd != FD_MAGIC_NUMBER) && (cu->fd != -1) && (FD_ISSET (cu->fd, &sfd)))
+#endif
{
log(DEBUG,"Data waiting on socket %d",cu->fd);
int MOD_RESULT = 0;
int currfd = current->fd;
int floodlines = 0;
// add the data to the users buffer
+ if (result > 0)
if (!current->AddBuffer(data))
{
// AddBuffer returned false, theres too much data in the user's buffer and theyre up to no good.
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)
{
sched_yield();
#endif
+#ifndef USE_KQUEUE
// set up select call
for (count = 0; count < boundPortCount; count++)
{
/* 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);
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;
}
}
}