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