int openSockfd[MAXSOCKS];
bool nofork = false;
bool unlimitcore = false;
+struct sockaddr_in client,server;
+char addrs[MAXBUF][255];
+socklen_t length;
+char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
time_t TIME = time(NULL), OLDTIME = time(NULL);
// 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;
}
}
-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))
{
{
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.
{
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)))
{
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))
{
{
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)
#endif
}
+
void CheckRoot()
{
if (geteuid() == 0)
}
}
+
+int BindPorts()
+{
+ int clientportcount = 0;
+ for (int count = 0; count < ConfValueEnum("bind",&config_f); count++)
+ {
+ ConfValue("bind","port",count,configToken,&config_f);
+ ConfValue("bind","address",count,Addr,&config_f);
+ ConfValue("bind","type",count,Type,&config_f);
+ if (strcmp(Type,"servers"))
+ {
+ // modules handle server bind types now,
+ // its not a typo in the strcmp.
+ ports[clientportcount] = atoi(configToken);
+ strlcpy(addrs[clientportcount],Addr,256);
+ clientportcount++;
+ log(DEBUG,"InspIRCd: startup: read binding %s:%s [%s] from config",Addr,configToken, Type);
+ }
+ }
+ portCount = clientportcount;
+
+ for (int count = 0; count < portCount; count++)
+ {
+ if ((openSockfd[boundPortCount] = OpenTCPSocket()) == ERROR)
+ {
+ log(DEBUG,"InspIRCd: startup: bad fd %lu",(unsigned long)openSockfd[boundPortCount]);
+ return(ERROR);
+ }
+ if (BindSocket(openSockfd[boundPortCount],client,server,ports[count],addrs[count]) == ERROR)
+ {
+ log(DEFAULT,"InspIRCd: startup: failed to bind port %lu",(unsigned long)ports[count]);
+ }
+ else /* well we at least bound to one socket so we'll continue */
+ {
+ boundPortCount++;
+ }
+ }
+
+ /* if we didn't bind to anything then abort */
+ if (!boundPortCount)
+ {
+ log(DEFAULT,"InspIRCd: startup: no ports bound, bailing!");
+ printf("\nERROR: Was not able to bind any of %lu ports! Please check your configuration.\n\n", (unsigned long)portCount);
+ return (ERROR);
+ }
+
+ return boundPortCount;
+}
+
+void CheckDie()
+{
+ if (DieValue[0])
+ {
+ printf("WARNING: %s\n\n",DieValue);
+ log(DEFAULT,"Ut-Oh, somebody didn't read their config file: '%s'",DieValue);
+ exit(0);
+ }
+}
+
+void LoadAllModules()
+{
+ /* We must load the modules AFTER initializing the socket engine, now */
+ MODCOUNT = -1;
+ for (int count = 0; count < ConfValueEnum("module",&config_f); count++)
+ {
+ ConfValue("module","name",count,configToken,&config_f);
+ printf("Loading module... \033[1;32m%s\033[0m\n",configToken);
+ if (!LoadModule(configToken))
+ {
+ log(DEFAULT,"Exiting due to a module loader error.");
+ printf("\nThere was an error loading a module: %s\n\nYou might want to do './inspircd start' instead of 'bin/inspircd'\n\n",ModuleError());
+ Exit(0);
+ }
+ }
+ log(DEFAULT,"Total loaded modules: %lu",(unsigned long)MODCOUNT+1);
+}
+
int InspIRCd(char** argv, int argc)
{
- struct sockaddr_in client,server;
- char addrs[MAXBUF][255];
+ bool expire_run = false;
+ std::vector<int> activefds;
int incomingSockfd;
- socklen_t length;
- int count = 0;
- int clientportcount = 0;
- char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
OpenLog(argv, argc);
CheckRoot();
SetupCommandTable();
ReadConfig(true,NULL);
AddServerName(ServerName);
-
- if (DieValue[0])
- {
- printf("WARNING: %s\n\n",DieValue);
- log(DEFAULT,"Ut-Oh, somebody didn't read their config file: '%s'",DieValue);
- exit(0);
- }
- log(DEBUG,"InspIRCd: startup: read config");
-
- for (count = 0; count < ConfValueEnum("bind",&config_f); count++)
- {
- ConfValue("bind","port",count,configToken,&config_f);
- ConfValue("bind","address",count,Addr,&config_f);
- ConfValue("bind","type",count,Type,&config_f);
- if (!strcmp(Type,"servers"))
- {
- // modules handle this bind type now.
- }
- else
- {
- ports[clientportcount] = atoi(configToken);
- strlcpy(addrs[clientportcount],Addr,256);
- clientportcount++;
- }
- log(DEBUG,"InspIRCd: startup: read binding %s:%s [%s] from config",Addr,configToken, Type);
- }
- portCount = clientportcount;
-
- log(DEBUG,"InspIRCd: startup: read %lu total client ports",(unsigned long)portCount);
+ CheckDie();
+ boundPortCount = BindPorts();
printf("\n");
startup_time = time(NULL);
ConfValue("pid","file",0,PID,&config_f);
// write once here, to try it out and make sure its ok
WritePID(PID);
-
- log(VERBOSE,"InspIRCd: startup: portCount = %lu", (unsigned long)portCount);
-
- for (count = 0; count < portCount; count++)
- {
- if ((openSockfd[boundPortCount] = OpenTCPSocket()) == ERROR)
- {
- log(DEBUG,"InspIRCd: startup: bad fd %lu",(unsigned long)openSockfd[boundPortCount]);
- return(ERROR);
- }
- if (BindSocket(openSockfd[boundPortCount],client,server,ports[count],addrs[count]) == ERROR)
- {
- log(DEFAULT,"InspIRCd: startup: failed to bind port %lu",(unsigned long)ports[count]);
- }
- else /* well we at least bound to one socket so we'll continue */
- {
- boundPortCount++;
- }
- }
- log(DEBUG,"InspIRCd: startup: total bound ports %lu",(unsigned long)boundPortCount);
-
- /* if we didn't bind to anything then abort */
- if (boundPortCount == 0)
- {
- log(DEFAULT,"InspIRCd: startup: no ports bound, bailing!");
- printf("\nERROR: Was not able to bind any of %lu ports! Please check your configuration.\n\n", (unsigned long)portCount);
- return (ERROR);
- }
-
- if (nofork)
- {
- log(VERBOSE,"Not forking as -nofork was specified");
- }
- else
+ if (!nofork)
{
if (DaemonSeed() == ERROR)
{
- log(DEFAULT,"InspIRCd: startup: can't daemonise");
printf("ERROR: could not go into daemon mode. Shutting down.\n");
Exit(ERROR);
}
}
+ /* Because of limitations in kqueue on freebsd, we must fork BEFORE we
+ * initialize the socket engine.
+ */
SE = new SocketEngine();
/* We must load the modules AFTER initializing the socket engine, now */
- MODCOUNT = -1;
- for (count = 0; count < ConfValueEnum("module",&config_f); count++)
- {
- ConfValue("module","name",count,configToken,&config_f);
- printf("Loading module... \033[1;32m%s\033[0m\n",configToken);
- if (!LoadModule(configToken))
- {
- log(DEFAULT,"Exiting due to a module loader error.");
- printf("\nThere was an error loading a module: %s\n\nYou might want to do './inspircd start' instead of 'bin/inspircd'\n\n",ModuleError());
- Exit(0);
- }
- }
- log(DEFAULT,"Total loaded modules: %lu",(unsigned long)MODCOUNT+1);
+ LoadAllModules();
printf("\nInspIRCd is now running!\n");
-
if (!nofork)
{
freopen("/dev/null","w",stdout);
/* Add the listening sockets used for client inbound connections
* to the socket engine
*/
- for (count = 0; count < portCount; count++)
+ for (int count = 0; count < portCount; count++)
SE->AddFd(openSockfd[count],true,X_LISTEN);
- std::vector<int> activefds;
-
WritePID(PID);
- bool expire_run = false;
/* main loop, this never returns */
for (;;)
{
#ifdef _POSIX_PRIORITY_SCHEDULING
- sched_yield();
+ /* 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);
-#ifndef THREADED_DNS
- dns_poll();
-#endif
-
- // *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);
- for (unsigned int activefd = 0; activefd < activefds.size(); activefd++)
+ /**
+ * 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++)
{
- if (SE->GetType(activefds[activefd]) == X_ESTAB_CLIENT)
- {
- 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 (SE->GetType(activefds[activefd]) == X_ESTAB_MODULE)
+ int socket_type = SE->GetType(activefds[activefd]);
+ switch (socket_type)
{
- 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->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 (SE->GetType(activefds[activefd]) == X_ESTAB_DNS)
- {
- log(DEBUG,"Got a ready socket of type X_ESTAB_DNS");
- }
- else if (SE->GetType(activefds[activefd]) == X_LISTEN)
- {
- log(DEBUG,"Got a ready socket of type X_LISTEN");
- /* It maybe a listener */
- for (count = 0; count < boundPortCount; count++)
- {
- if (activefds[activefd] == openSockfd[count])
+
+ 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]);
+#endif
+ 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)
+ if (activefds[activefd] == openSockfd[count])
{
- 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
- {
- 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;
}
}