]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd.cpp
20dd162e533d61876eb4a39503a94fceb6024ed7
[user/henk/code/inspircd.git] / src / inspircd.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "configreader.h"
16 #include <signal.h>
17 #include <dirent.h>
18 #include <exception>
19 #include <fstream>
20 #include "modules.h"
21 #include "mode.h"
22 #include "xline.h"
23 #include "socketengine.h"
24 #include "inspircd_se_config.h"
25 #include "socket.h"
26 #include "typedefs.h"
27 #include "command_parse.h"
28 #include "exitcodes.h"
29 #include <dlfcn.h>
30
31 using irc::sockets::NonBlocking;
32 using irc::sockets::Blocking;
33 using irc::sockets::insp_ntoa;
34 using irc::sockets::insp_inaddr;
35 using irc::sockets::insp_sockaddr;
36
37 InspIRCd* SI = NULL;
38
39 void InspIRCd::AddServerName(const std::string &servername)
40 {
41         this->Log(DEBUG,"Adding server name: %s",servername.c_str());
42         
43         if(find(servernames.begin(), servernames.end(), servername) == servernames.end())
44                 servernames.push_back(servername); /* Wasn't already there. */
45 }
46
47 const char* InspIRCd::FindServerNamePtr(const std::string &servername)
48 {
49         servernamelist::iterator iter = find(servernames.begin(), servernames.end(), servername);
50         
51         if(iter == servernames.end())
52         {               
53                 AddServerName(servername);
54                 iter = --servernames.end();
55         }
56
57         return iter->c_str();
58 }
59
60 bool InspIRCd::FindServerName(const std::string &servername)
61 {
62         return (find(servernames.begin(), servernames.end(), servername) != servernames.end());
63 }
64
65 void InspIRCd::Exit(int status)
66 {
67         if (SI)
68         {
69                 SI->SendError("Exiting with status " + ConvToStr(status) + " (" + std::string(ExitCodes[status]) + ")");
70                 SI->Cleanup();
71         }
72         exit (status);
73 }
74
75 void InspIRCd::Cleanup()
76 {
77         std::vector<std::string> mymodnames;
78         int MyModCount = this->GetModuleCount();
79
80         for (unsigned int i = 0; i < stats->BoundPortCount; i++)
81                 /* This calls the constructor and closes the listening socket */
82                 delete Config->openSockfd[i];
83
84         /* We do this more than once, so that any service providers get a
85          * chance to be unhooked by the modules using them, but then get
86          * a chance to be removed themsleves.
87          */
88         for (int tries = 0; tries < 3; tries++)
89         {
90                 MyModCount = this->GetModuleCount();
91                 mymodnames.clear();
92
93                 /* Unload all modules, so they get a chance to clean up their listeners */
94                 for (int j = 0; j <= MyModCount; j++)
95                         mymodnames.push_back(Config->module_names[j]);
96
97                 for (int k = 0; k <= MyModCount; k++)
98                         this->UnloadModule(mymodnames[k].c_str());
99         }
100
101         /* Close all client sockets, or the new process inherits them */
102         for (std::vector<userrec*>::const_iterator i = this->local_users.begin(); i != this->local_users.end(); i++)
103                 (*i)->CloseSocket();
104
105         /* Close logging */
106         this->Logger->Close();
107 }
108
109 void InspIRCd::Restart(const std::string &reason)
110 {
111         /* SendError flushes each client's queue,
112          * regardless of writeability state
113          */
114         this->SendError(reason);
115
116         this->Cleanup();
117
118         /* Figure out our filename (if theyve renamed it, we're boned) */
119         std::string me = Config->MyDir + "/inspircd";
120
121         if (execv(me.c_str(), Config->argv) == -1)
122         {
123                 /* Will raise a SIGABRT if not trapped */
124                 throw CoreException(std::string("Failed to execv()! error: ") + strerror(errno));
125         }
126 }
127
128 void InspIRCd::Start()
129 {
130         printf("\033[1;32mInspire Internet Relay Chat Server, compiled %s at %s\n",__DATE__,__TIME__);
131         printf("(C) InspIRCd Development Team.\033[0m\n\n");
132         printf("Developers:\t\t\033[1;32mBrain, FrostyCoolSlug, w00t, Om, Special, pippijn, peavey\033[0m\n");
133         printf("Others:\t\t\t\033[1;32mSee /INFO Output\033[0m\n");
134 }
135
136 void InspIRCd::Rehash(int status)
137 {
138         SI->WriteOpers("Rehashing config file %s due to SIGHUP",ServerConfig::CleanFilename(CONFIG_FILE));
139         SI->CloseLog();
140         SI->OpenLog(NULL,0);
141         SI->RehashUsersAndChans();
142         SI->Config->Read(false,NULL);
143         FOREACH_MOD_I(SI,I_OnRehash,OnRehash(""));
144 }
145
146 /** Because hash_map doesnt free its buckets when we delete items (this is a 'feature')
147  * we must occasionally rehash the hash (yes really).
148  * We do this by copying the entries from the old hash to a new hash, causing all
149  * empty buckets to be weeded out of the hash. We dont do this on a timer, as its
150  * very expensive, so instead we do it when the user types /REHASH and expects a
151  * short delay anyway.
152  */
153 void InspIRCd::RehashUsersAndChans()
154 {
155         user_hash* old_users = this->clientlist;
156         chan_hash* old_chans = this->chanlist;
157
158         this->clientlist = new user_hash();
159         this->chanlist = new chan_hash();
160
161         for (user_hash::const_iterator n = old_users->begin(); n != old_users->end(); n++)
162                 this->clientlist->insert(*n);
163
164         delete old_users;
165
166         for (chan_hash::const_iterator n = old_chans->begin(); n != old_chans->end(); n++)
167                 this->chanlist->insert(*n);
168
169         delete old_chans;
170 }
171
172 void InspIRCd::CloseLog()
173 {
174         this->Logger->Close();
175 }
176
177 void InspIRCd::SetSignals()
178 {
179         signal(SIGALRM, SIG_IGN);
180         signal(SIGHUP, InspIRCd::Rehash);
181         signal(SIGPIPE, SIG_IGN);
182         signal(SIGTERM, InspIRCd::Exit);
183         signal(SIGCHLD, SIG_IGN);
184 }
185
186 bool InspIRCd::DaemonSeed()
187 {
188         int childpid;
189         if ((childpid = fork ()) < 0)
190                 return false;
191         else if (childpid > 0)
192         {
193                 /* We wait here for the child process to kill us,
194                  * so that the shell prompt doesnt come back over
195                  * the output.
196                  * Sending a kill with a signal of 0 just checks
197                  * if the child pid is still around. If theyre not,
198                  * they threw an error and we should give up.
199                  */
200                 while (kill(childpid, 0) != -1)
201                         sleep(1);
202                 exit(0);
203         }
204         setsid ();
205         umask (007);
206         printf("InspIRCd Process ID: \033[1;32m%lu\033[0m\n",(unsigned long)getpid());
207
208         rlimit rl;
209         if (getrlimit(RLIMIT_CORE, &rl) == -1)
210         {
211                 this->Log(DEFAULT,"Failed to getrlimit()!");
212                 return false;
213         }
214         else
215         {
216                 rl.rlim_cur = rl.rlim_max;
217                 if (setrlimit(RLIMIT_CORE, &rl) == -1)
218                         this->Log(DEFAULT,"setrlimit() failed, cannot increase coredump size.");
219         }
220
221         return true;
222 }
223
224 void InspIRCd::WritePID(const std::string &filename)
225 {
226         std::string fname = (filename.empty() ? "inspircd.pid" : filename);
227         if (*(fname.begin()) != '/')
228         {
229                 std::string::size_type pos;
230                 std::string confpath = CONFIG_FILE;
231                 if ((pos = confpath.find("/inspircd.conf")) != std::string::npos)
232                 {
233                         /* Leaves us with just the path */
234                         fname = confpath.substr(0, pos) + std::string("/") + fname;
235                 }
236         }                                                                       
237         std::ofstream outfile(fname.c_str());
238         if (outfile.is_open())
239         {
240                 outfile << getpid();
241                 outfile.close();
242         }
243         else
244         {
245                 printf("Failed to write PID-file '%s', exiting.\n",fname.c_str());
246                 this->Log(DEFAULT,"Failed to write PID-file '%s', exiting.",fname.c_str());
247                 Exit(EXIT_STATUS_PID);
248         }
249 }
250
251 std::string InspIRCd::GetRevision()
252 {
253         return REVISION;
254 }
255
256 InspIRCd::InspIRCd(int argc, char** argv)
257         : ModCount(-1), duration_m(60), duration_h(60*60), duration_d(60*60*24), duration_w(60*60*24*7), duration_y(60*60*24*365)
258 {
259         int found_ports = 0;
260         FailedPortList pl;
261
262         modules.resize(255);
263         factory.resize(255);
264
265         this->clientlist = new user_hash();
266         this->chanlist = new chan_hash();
267
268         this->Config = new ServerConfig(this);
269
270         this->Config->argv = argv;
271         this->Config->argc = argc;
272
273         this->Config->opertypes.clear();
274         this->Config->operclass.clear();
275         this->SNO = new SnomaskManager(this);
276         this->Start();
277         this->TIME = this->OLDTIME = this->startup_time = time(NULL);
278         this->time_delta = 0;
279         this->next_call = this->TIME + 3;
280         srand(this->TIME);
281         this->Log(DEBUG,"*** InspIRCd starting up!");
282         if (!ServerConfig::FileExists(CONFIG_FILE))
283         {
284                 printf("ERROR: Cannot open config file: %s\nExiting...\n",CONFIG_FILE);
285                 this->Log(DEFAULT,"main: no config");
286                 printf("ERROR: Your config file is missing, this IRCd will self destruct in 10 seconds!\n");
287                 Exit(EXIT_STATUS_CONFIG);
288         }
289         *this->LogFileName = 0;
290         if (argc > 1) {
291                 for (int i = 1; i < argc; i++)
292                 {
293                         if (!strcmp(argv[i],"-nofork"))
294                         {
295                                 Config->nofork = true;
296                         }
297                         else if(!strcmp(argv[i],"-debug"))
298                         {
299                                 Config->forcedebug = true;
300                         }
301                         else if(!strcmp(argv[i],"-nolog"))
302                         {
303                                 Config->writelog = false;
304                         }
305                         else if (!strcmp(argv[i],"-wait"))
306                         {
307                                 sleep(6);
308                         }
309                         else if (!strcmp(argv[i],"-logfile"))
310                         {
311                                 if (argc > i+1)
312                                 {
313                                         strlcpy(LogFileName,argv[i+1],MAXBUF);
314                                         printf("LOG: Setting logfile to %s\n",LogFileName);
315                                 }
316                                 else
317                                 {
318                                         printf("ERROR: The -logfile parameter must be followed by a log file name and path.\n");
319                                         Exit(EXIT_STATUS_CONFIG);
320                                 }
321                                 i++;
322                         }
323                         else
324                         {
325                                 printf("Usage: %s [-nofork] [-nolog] [-debug] [-wait] [-logfile <filename>]\n",argv[0]);
326                                 Exit(EXIT_STATUS_ARGV);
327                         }
328                 }
329         }
330
331         strlcpy(Config->MyExecutable,argv[0],MAXBUF);
332
333         this->OpenLog(argv, argc);
334         this->stats = new serverstats();
335         this->Parser = new CommandParser(this);
336         this->Timers = new TimerManager();
337         this->XLines = new XLineManager(this);
338         Config->ClearStack();
339         Config->Read(true, NULL);
340         this->CheckRoot();
341         this->Modes = new ModeParser(this);
342         this->AddServerName(Config->ServerName);        
343         CheckDie();
344         InitializeDisabledCommands(Config->DisabledCommands, this);
345         stats->BoundPortCount = BindPorts(true, found_ports, pl);
346
347         for(int t = 0; t < 255; t++)
348                 Config->global_implementation[t] = 0;
349
350         memset(&Config->implement_lists,0,sizeof(Config->implement_lists));
351
352         printf("\n");
353         this->SetSignals();
354         if (!Config->nofork)
355         {
356                 if (!this->DaemonSeed())
357                 {
358                         printf("ERROR: could not go into daemon mode. Shutting down.\n");
359                         Exit(EXIT_STATUS_FORK);
360                 }
361         }
362
363         /* Because of limitations in kqueue on freebsd, we must fork BEFORE we
364          * initialize the socket engine.
365          */
366         SocketEngineFactory* SEF = new SocketEngineFactory();
367         SE = SEF->Create(this);
368         delete SEF;
369
370         this->Res = new DNS(this);
371
372         this->LoadAllModules();
373         /* Just in case no modules were loaded - fix for bug #101 */
374         this->BuildISupport();
375
376         if ((stats->BoundPortCount == 0) && (found_ports > 0))
377         {
378                 printf("\nERROR: I couldn't bind any ports! Are you sure you didn't start InspIRCd twice?\n");
379                 Exit(EXIT_STATUS_BIND);
380         }
381         
382         if (stats->BoundPortCount != (unsigned int)found_ports)
383         {
384                 printf("\nWARNING: Not all your client ports could be bound --\nstarting anyway with %ld of %d client ports bound.\n\n", stats->BoundPortCount, found_ports);
385                 printf("The following port%s failed to bind:\n", found_ports - stats->BoundPortCount != 1 ? "s" : "");
386                 int j = 1;
387                 for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
388                 {
389                         printf("%d.\tIP: %s\tPort: %lu\n", j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second);
390                 }
391         }
392
393         /* Add the listening sockets used for client inbound connections
394          * to the socket engine
395          */
396         this->Log(DEBUG,"%d listeners",stats->BoundPortCount);
397         for (unsigned long count = 0; count < stats->BoundPortCount; count++)
398         {
399                 this->Log(DEBUG,"Add listener: %d",Config->openSockfd[count]->GetFd());
400                 if (!SE->AddFd(Config->openSockfd[count]))
401                 {
402                         printf("\nEH? Could not add listener to socketengine. You screwed up, aborting.\n");
403                         Exit(EXIT_STATUS_INTERNAL);
404                 }
405         }
406
407         if (!Config->nofork)
408         {
409                 if (kill(getppid(), SIGTERM) == -1)
410                         printf("Error killing parent process: %s\n",strerror(errno));
411                 fclose(stdin);
412                 fclose(stderr);
413                 fclose(stdout);
414         }
415
416         printf("\nInspIRCd is now running!\n");
417
418         this->WritePID(Config->PID);
419 }
420
421 std::string InspIRCd::GetVersionString()
422 {
423         char versiondata[MAXBUF];
424         char dnsengine[] = "singlethread-object";
425         if (*Config->CustomVersion)
426         {
427                 snprintf(versiondata,MAXBUF,"%s %s :%s",VERSION,Config->ServerName,Config->CustomVersion);
428         }
429         else
430         {
431                 snprintf(versiondata,MAXBUF,"%s %s :%s [FLAGS=%lu,%s,%s]",VERSION,Config->ServerName,SYSTEM,(unsigned long)OPTIMISATION,SE->GetName().c_str(),dnsengine);
432         }
433         return versiondata;
434 }
435
436 char* InspIRCd::ModuleError()
437 {
438         return MODERR;
439 }
440
441 void InspIRCd::EraseFactory(int j)
442 {
443         int v = 0;
444         for (std::vector<ircd_module*>::iterator t = factory.begin(); t != factory.end(); t++)
445         {
446                 if (v == j)
447                 {
448                         delete *t;
449                         factory.erase(t);
450                         factory.push_back(NULL);
451                         return;
452                 }
453                 v++;
454         }
455 }
456
457 void InspIRCd::EraseModule(int j)
458 {
459         int v1 = 0;
460         for (ModuleList::iterator m = modules.begin(); m!= modules.end(); m++)
461         {
462                 if (v1 == j)
463                 {
464                         DELETE(*m);
465                         modules.erase(m);
466                         modules.push_back(NULL);
467                         break;
468                 }
469                 v1++;
470         }
471         int v2 = 0;
472         for (std::vector<std::string>::iterator v = Config->module_names.begin(); v != Config->module_names.end(); v++)
473         {
474                 if (v2 == j)
475                 {
476                        Config->module_names.erase(v);
477                        break;
478                 }
479                 v2++;
480         }
481
482 }
483
484 void InspIRCd::MoveTo(std::string modulename,int slot)
485 {
486         unsigned int v2 = 256;
487         for (unsigned int v = 0; v < Config->module_names.size(); v++)
488         {
489                 if (Config->module_names[v] == modulename)
490                 {
491                         // found an instance, swap it with the item at the end
492                         v2 = v;
493                         break;
494                 }
495         }
496         if ((v2 != (unsigned int)slot) && (v2 < 256))
497         {
498                 // Swap the module names over
499                 Config->module_names[v2] = Config->module_names[slot];
500                 Config->module_names[slot] = modulename;
501                 // now swap the module factories
502                 ircd_module* temp = factory[v2];
503                 factory[v2] = factory[slot];
504                 factory[slot] = temp;
505                 // now swap the module objects
506                 Module* temp_module = modules[v2];
507                 modules[v2] = modules[slot];
508                 modules[slot] = temp_module;
509                 // now swap the implement lists (we dont
510                 // need to swap the global or recount it)
511                 for (int n = 0; n < 255; n++)
512                 {
513                         char x = Config->implement_lists[v2][n];
514                         Config->implement_lists[v2][n] = Config->implement_lists[slot][n];
515                         Config->implement_lists[slot][n] = x;
516                 }
517         }
518         else
519         {
520                 this->Log(DEBUG,"Move of %s to slot failed!",modulename.c_str());
521         }
522 }
523
524 void InspIRCd::MoveAfter(std::string modulename, std::string after)
525 {
526         for (unsigned int v = 0; v < Config->module_names.size(); v++)
527         {
528                 if (Config->module_names[v] == after)
529                 {
530                         MoveTo(modulename, v);
531                         return;
532                 }
533         }
534 }
535
536 void InspIRCd::MoveBefore(std::string modulename, std::string before)
537 {
538         for (unsigned int v = 0; v < Config->module_names.size(); v++)
539         {
540                 if (Config->module_names[v] == before)
541                 {
542                         if (v > 0)
543                         {
544                                 MoveTo(modulename, v-1);
545                         }
546                         else
547                         {
548                                 MoveTo(modulename, v);
549                         }
550                         return;
551                 }
552         }
553 }
554
555 void InspIRCd::MoveToFirst(std::string modulename)
556 {
557         MoveTo(modulename,0);
558 }
559
560 void InspIRCd::MoveToLast(std::string modulename)
561 {
562         MoveTo(modulename,this->GetModuleCount());
563 }
564
565 void InspIRCd::BuildISupport()
566 {
567         // the neatest way to construct the initial 005 numeric, considering the number of configure constants to go in it...
568         std::stringstream v;
569         v << "WALLCHOPS WALLVOICES MODES=" << MAXMODES << " CHANTYPES=# PREFIX=" << this->Modes->BuildPrefixes() << " MAP MAXCHANNELS=" << MAXCHANS << " MAXBANS=60 VBANLIST NICKLEN=" << NICKMAX-1;
570         v << " CASEMAPPING=rfc1459 STATUSMSG=@%+ CHARSET=ascii TOPICLEN=" << MAXTOPIC << " KICKLEN=" << MAXKICK << " MAXTARGETS=" << Config->MaxTargets << " AWAYLEN=";
571         v << MAXAWAY << " CHANMODES=" << this->Modes->ChanModes() << " FNC NETWORK=" << Config->Network << " MAXPARA=32";
572         Config->data005 = v.str();
573         FOREACH_MOD_I(this,I_On005Numeric,On005Numeric(Config->data005));
574         Config->Update005();
575 }
576
577 bool InspIRCd::UnloadModule(const char* filename)
578 {
579         std::string filename_str = filename;
580         for (unsigned int j = 0; j != Config->module_names.size(); j++)
581         {
582                 if (Config->module_names[j] == filename_str)
583                 {
584                         if (modules[j]->GetVersion().Flags & VF_STATIC)
585                         {
586                                 this->Log(DEFAULT,"Failed to unload STATIC module %s",filename);
587                                 snprintf(MODERR,MAXBUF,"Module not unloadable (marked static)");
588                                 return false;
589                         }
590                         std::pair<int,std::string> intercount = GetInterfaceInstanceCount(modules[j]);
591                         if (intercount.first > 0)
592                         {
593                                 this->Log(DEFAULT,"Failed to unload module %s, being used by %d other(s) via interface '%s'",filename, intercount.first, intercount.second.c_str());
594                                 snprintf(MODERR,MAXBUF,"Module not unloadable (Still in use by %d other module%s which %s using its interface '%s') -- unload dependent modules first!",
595                                                 intercount.first,
596                                                 intercount.first > 1 ? "s" : "",
597                                                 intercount.first > 1 ? "are" : "is",
598                                                 intercount.second.c_str());
599                                 return false;
600                         }
601                         /* Give the module a chance to tidy out all its metadata */
602                         for (chan_hash::iterator c = this->chanlist->begin(); c != this->chanlist->end(); c++)
603                         {
604                                 modules[j]->OnCleanup(TYPE_CHANNEL,c->second);
605                         }
606                         for (user_hash::iterator u = this->clientlist->begin(); u != this->clientlist->end(); u++)
607                         {
608                                 modules[j]->OnCleanup(TYPE_USER,u->second);
609                         }
610
611                         /* Tidy up any dangling resolvers */
612                         this->Res->CleanResolvers(modules[j]);
613
614                         FOREACH_MOD_I(this,I_OnUnloadModule,OnUnloadModule(modules[j],Config->module_names[j]));
615
616                         for(int t = 0; t < 255; t++)
617                         {
618                                 Config->global_implementation[t] -= Config->implement_lists[j][t];
619                         }
620
621                         /* We have to renumber implement_lists after unload because the module numbers change!
622                          */
623                         for(int j2 = j; j2 < 254; j2++)
624                         {
625                                 for(int t = 0; t < 255; t++)
626                                 {
627                                         Config->implement_lists[j2][t] = Config->implement_lists[j2+1][t];
628                                 }
629                         }
630
631                         // found the module
632                         Parser->RemoveCommands(filename);
633                         this->EraseModule(j);
634                         this->EraseFactory(j);
635                         this->Log(DEFAULT,"Module %s unloaded",filename);
636                         this->ModCount--;
637                         BuildISupport();
638                         return true;
639                 }
640         }
641         this->Log(DEFAULT,"Module %s is not loaded, cannot unload it!",filename);
642         snprintf(MODERR,MAXBUF,"Module not loaded");
643         return false;
644 }
645
646 bool InspIRCd::LoadModule(const char* filename)
647 {
648         /* Do we have a glob pattern in the filename?
649          * The user wants to load multiple modules which
650          * match the pattern.
651          */
652         if (strchr(filename,'*') || (strchr(filename,'?')))
653         {
654                 int n_match = 0;
655                 DIR* library = opendir(Config->ModPath);
656                 if (library)
657                 {
658                         /* Try and locate and load all modules matching the pattern */
659                         dirent* entry = NULL;
660                         while ((entry = readdir(library)))
661                         {
662                                 if (this->MatchText(entry->d_name, filename))
663                                 {
664                                         if (!this->LoadModule(entry->d_name))
665                                                 n_match++;
666                                 }
667                         }
668                         closedir(library);
669                 }
670                 /* Loadmodule will now return false if any one of the modules failed
671                  * to load (but wont abort when it encounters a bad one) and when 1 or
672                  * more modules were actually loaded.
673                  */
674                 return (n_match > 0);
675         }
676
677         char modfile[MAXBUF];
678         snprintf(modfile,MAXBUF,"%s/%s",Config->ModPath,filename);
679         std::string filename_str = filename;
680
681         if (!ServerConfig::DirValid(modfile))
682         {
683                 this->Log(DEFAULT,"Module %s is not within the modules directory.",modfile);
684                 snprintf(MODERR,MAXBUF,"Module %s is not within the modules directory.",modfile);
685                 return false;
686         }
687         this->Log(DEBUG,"Loading module: %s",modfile);
688
689         if (ServerConfig::FileExists(modfile))
690         {
691
692                 for (unsigned int j = 0; j < Config->module_names.size(); j++)
693                 {
694                         if (Config->module_names[j] == filename_str)
695                         {
696                                 this->Log(DEFAULT,"Module %s is already loaded, cannot load a module twice!",modfile);
697                                 snprintf(MODERR,MAXBUF,"Module already loaded");
698                                 return false;
699                         }
700                 }
701                 try
702                 {
703                         ircd_module* a = new ircd_module(this, modfile);
704                         factory[this->ModCount+1] = a;
705                         if (factory[this->ModCount+1]->LastError())
706                         {
707                                 this->Log(DEFAULT,"Unable to load %s: %s",modfile,factory[this->ModCount+1]->LastError());
708                                 snprintf(MODERR,MAXBUF,"Loader/Linker error: %s",factory[this->ModCount+1]->LastError());
709                                 return false;
710                         }
711                         if ((long)factory[this->ModCount+1]->factory != -1)
712                         {
713                                 Module* m = factory[this->ModCount+1]->factory->CreateModule(this);
714
715                                 Version v = m->GetVersion();
716
717                                 if (v.API != API_VERSION)
718                                 {
719                                         delete m;
720                                         delete a;
721                                         this->Log(DEFAULT,"Unable to load %s: Incorrect module API version: %d (our version: %d)",modfile,v.API,API_VERSION);
722                                         snprintf(MODERR,MAXBUF,"Loader/Linker error: Incorrect module API version: %d (our version: %d)",v.API,API_VERSION);
723                                         return false;
724                                 }
725                                 else
726                                 {
727                                         this->Log(DEFAULT,"New module introduced: %s (API version %d, Module version %d.%d.%d.%d)%s", filename, v.API, v.Major, v.Minor, v.Revision, v.Build, (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
728                                 }
729
730                                 modules[this->ModCount+1] = m;
731                                 /* save the module and the module's classfactory, if
732                                  * this isnt done, random crashes can occur :/ */
733                                 Config->module_names.push_back(filename);
734
735                                 char* x = &Config->implement_lists[this->ModCount+1][0];
736                                 for(int t = 0; t < 255; t++)
737                                         x[t] = 0;
738
739                                 modules[this->ModCount+1]->Implements(x);
740
741                                 for(int t = 0; t < 255; t++)
742                                         Config->global_implementation[t] += Config->implement_lists[this->ModCount+1][t];
743                         }
744                         else
745                         {
746                                 this->Log(DEFAULT,"Unable to load %s",modfile);
747                                 snprintf(MODERR,MAXBUF,"Factory function failed: Probably missing init_module() entrypoint.");
748                                 return false;
749                         }
750                 }
751                 catch (CoreException& modexcept)
752                 {
753                         this->Log(DEFAULT,"Unable to load %s: ",modfile,modexcept.GetReason());
754                         snprintf(MODERR,MAXBUF,"Factory function of %s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
755                         return false;
756                 }
757         }
758         else
759         {
760                 this->Log(DEFAULT,"InspIRCd: startup: Module Not Found %s",modfile);
761                 snprintf(MODERR,MAXBUF,"Module file could not be found");
762                 return false;
763         }
764         this->ModCount++;
765         FOREACH_MOD_I(this,I_OnLoadModule,OnLoadModule(modules[this->ModCount],filename_str));
766         // now work out which modules, if any, want to move to the back of the queue,
767         // and if they do, move them there.
768         std::vector<std::string> put_to_back;
769         std::vector<std::string> put_to_front;
770         std::map<std::string,std::string> put_before;
771         std::map<std::string,std::string> put_after;
772         for (unsigned int j = 0; j < Config->module_names.size(); j++)
773         {
774                 if (modules[j]->Prioritize() == PRIORITY_LAST)
775                         put_to_back.push_back(Config->module_names[j]);
776                 else if (modules[j]->Prioritize() == PRIORITY_FIRST)
777                         put_to_front.push_back(Config->module_names[j]);
778                 else if ((modules[j]->Prioritize() & 0xFF) == PRIORITY_BEFORE)
779                         put_before[Config->module_names[j]] = Config->module_names[modules[j]->Prioritize() >> 8];
780                 else if ((modules[j]->Prioritize() & 0xFF) == PRIORITY_AFTER)
781                         put_after[Config->module_names[j]] = Config->module_names[modules[j]->Prioritize() >> 8];
782         }
783         for (unsigned int j = 0; j < put_to_back.size(); j++)
784                 MoveToLast(put_to_back[j]);
785         for (unsigned int j = 0; j < put_to_front.size(); j++)
786                 MoveToFirst(put_to_front[j]);
787         for (std::map<std::string,std::string>::iterator j = put_before.begin(); j != put_before.end(); j++)
788                 MoveBefore(j->first,j->second);
789         for (std::map<std::string,std::string>::iterator j = put_after.begin(); j != put_after.end(); j++)
790                 MoveAfter(j->first,j->second);
791         BuildISupport();
792         return true;
793 }
794
795 void InspIRCd::DoOneIteration(bool process_module_sockets)
796 {
797         static rusage ru;
798
799         /* time() seems to be a pretty expensive syscall, so avoid calling it too much.
800          * Once per loop iteration is pleanty.
801          */
802         OLDTIME = TIME;
803         TIME = time(NULL);
804
805         /* Run background module timers every few seconds
806          * (the docs say modules shouldnt rely on accurate
807          * timing using this event, so we dont have to
808          * time this exactly).
809          */
810         if (TIME != OLDTIME)
811         {
812                 if (TIME < OLDTIME)
813                         WriteOpers("*** \002EH?!\002 -- Time is flowing BACKWARDS in this dimension! Clock drifted backwards %d secs.",abs(OLDTIME-TIME));
814                 if ((TIME % 3600) == 0)
815                 {
816                         irc::whowas::MaintainWhoWas(this, TIME);
817                         this->RehashUsersAndChans();
818                         FOREACH_MOD_I(this, I_OnGarbageCollect, OnGarbageCollect());
819                 }
820                 Timers->TickTimers(TIME);
821                 this->DoBackgroundUserStuff(TIME);
822
823                 if ((TIME % 5) == 0)
824                 {
825                         XLines->expire_lines();
826                         FOREACH_MOD_I(this,I_OnBackgroundTimer,OnBackgroundTimer(TIME));
827                         Timers->TickMissedTimers(TIME);
828                 }
829
830                 if (!getrusage(0, &ru))
831                 {
832                         gettimeofday(&this->stats->LastSampled, NULL);
833                         this->stats->LastCPU = ru.ru_utime;
834                 }
835         }
836
837         /* Call the socket engine to wait on the active
838          * file descriptors. The socket engine has everything's
839          * descriptors in its list... dns, modules, users,
840          * servers... so its nice and easy, just one call.
841          * This will cause any read or write events to be 
842          * dispatched to their handlers.
843          */
844         SE->DispatchEvents();
845 }
846
847 bool InspIRCd::IsIdent(const char* n)
848 {
849         if (!n || !*n)
850                 return false;
851
852         for (char* i = (char*)n; *i; i++)
853         {
854                 if ((*i >= 'A') && (*i <= '}'))
855                 {
856                         continue;
857                 }
858                 if (((*i >= '0') && (*i <= '9')) || (*i == '-') || (*i == '.'))
859                 {
860                         continue;
861                 }
862                 return false;
863         }
864         return true;
865 }
866
867
868 int InspIRCd::Run()
869 {
870         while (true)
871         {
872                 DoOneIteration(true);
873         }
874         /* This is never reached -- we hope! */
875         return 0;
876 }
877
878 /**********************************************************************************/
879
880 /**
881  * An ircd in four lines! bwahahaha. ahahahahaha. ahahah *cough*.
882  */
883
884 int main(int argc, char** argv)
885 {
886         SI = new InspIRCd(argc, argv);
887         SI->Run();
888         delete SI;
889         return 0;
890 }
891
892 /* this returns true when all modules are satisfied that the user should be allowed onto the irc server
893  * (until this returns true, a user will block in the waiting state, waiting to connect up to the
894  * registration timeout maximum seconds)
895  */
896 bool InspIRCd::AllModulesReportReady(userrec* user)
897 {
898         if (!Config->global_implementation[I_OnCheckReady])
899                 return true;
900
901         for (int i = 0; i <= this->GetModuleCount(); i++)
902         {
903                 if (Config->implement_lists[i][I_OnCheckReady])
904                 {
905                         int res = modules[i]->OnCheckReady(user);
906                         if (!res)
907                                 return false;
908                 }
909         }
910         return true;
911 }
912
913 int InspIRCd::GetModuleCount()
914 {
915         return this->ModCount;
916 }
917
918 time_t InspIRCd::Time(bool delta)
919 {
920         if (delta)
921                 return TIME + time_delta;
922         return TIME;
923 }
924
925 int InspIRCd::SetTimeDelta(int delta)
926 {
927         int old = time_delta;
928         time_delta += delta;
929         this->Log(DEBUG, "Time delta set to %d (was %d)", time_delta, old);
930         return old;
931 }
932
933 void InspIRCd::AddLocalClone(userrec* user)
934 {
935         clonemap::iterator x = local_clones.find(user->GetIPString());
936         if (x != local_clones.end())
937                 x->second++;
938         else
939                 local_clones[user->GetIPString()] = 1;
940 }
941
942 void InspIRCd::AddGlobalClone(userrec* user)
943 {
944         clonemap::iterator y = global_clones.find(user->GetIPString());
945         if (y != global_clones.end())
946                 y->second++;
947         else
948                 global_clones[user->GetIPString()] = 1;
949 }
950
951 int InspIRCd::GetTimeDelta()
952 {
953         return time_delta;
954 }
955
956 bool FileLogger::Readable()
957 {
958         return false;
959 }
960
961 void FileLogger::HandleEvent(EventType et, int errornum)
962 {
963         this->WriteLogLine("");
964         if (log)
965                 ServerInstance->SE->DelFd(this);
966 }
967
968 void FileLogger::WriteLogLine(const std::string &line)
969 {
970         if (line.length())
971                 buffer.append(line);
972
973         if (log)
974         {
975                 int written = fprintf(log,"%s",buffer.c_str());
976                 if ((written >= 0) && (written < (int)buffer.length()))
977                 {
978                         buffer.erase(0, buffer.length());
979                         ServerInstance->SE->AddFd(this);
980                 }
981                 else if (written == -1)
982                 {
983                         if (errno == EAGAIN)
984                                 ServerInstance->SE->AddFd(this);
985                 }
986                 else
987                 {
988                         /* Wrote the whole buffer, and no need for write callback */
989                         buffer = "";
990                 }
991         }
992         if (writeops++ % 20)
993         {
994                 fflush(log);
995         }
996 }
997
998 void FileLogger::Close()
999 {
1000         if (log)
1001         {
1002                 int flags = fcntl(fileno(log), F_GETFL, 0);
1003                 fcntl(fileno(log), F_SETFL, flags ^ O_NONBLOCK);
1004                 if (buffer.size())
1005                         fprintf(log,"%s",buffer.c_str());
1006
1007                 ServerInstance->SE->DelFd(this);
1008
1009                 fflush(log);
1010                 fclose(log);
1011         }
1012
1013         buffer = "";
1014 }
1015
1016 FileLogger::FileLogger(InspIRCd* Instance, FILE* logfile) : ServerInstance(Instance), log(logfile), writeops(0)
1017 {
1018         if (log)
1019         {
1020                 irc::sockets::NonBlocking(fileno(log));
1021                 this->SetFd(fileno(log));
1022                 buffer = "";
1023         }
1024 }
1025
1026 FileLogger::~FileLogger()
1027 {
1028         this->Close();
1029 }
1030