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