]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd.cpp
remove debug
[user/henk/code/inspircd.git] / src / inspircd.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 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 /* $Install: src/inspircd $(BINPATH) */
15 #include "inspircd.h"
16 #include <signal.h>
17
18 #ifndef WIN32
19         #include <dirent.h>
20         #include <unistd.h>
21         #include <sys/resource.h>
22         #include <dlfcn.h>
23         #include <getopt.h>
24
25         /* Some systems don't define RUSAGE_SELF. This should fix them. */
26         #ifndef RUSAGE_SELF
27                 #define RUSAGE_SELF 0
28         #endif
29
30         /* CRT memory debugging */
31         #ifdef DEBUG
32         #define _CRTDBG_MAP_ALLOC
33         #include <stdlib.h>
34         #include <crtdbg.h>
35         #endif
36 #endif
37
38 #include <fstream>
39 #include "xline.h"
40 #include "bancache.h"
41 #include "socketengine.h"
42 #include "inspircd_se_config.h"
43 #include "socket.h"
44 #include "command_parse.h"
45 #include "exitcodes.h"
46 #include "caller.h"
47 #include "testsuite.h"
48
49 using irc::sockets::insp_ntoa;
50 using irc::sockets::insp_inaddr;
51 using irc::sockets::insp_sockaddr;
52
53 InspIRCd* SI = NULL;
54 int* mysig = NULL;
55
56
57 /* Moved from exitcodes.h -- due to duplicate symbols -- Burlex
58  * XXX this is a bit ugly. -- w00t
59  */
60 const char* ExitCodes[] =
61 {
62                 "No error", /* 0 */
63                 "DIE command", /* 1 */
64                 "execv() failed", /* 2 */
65                 "Internal error", /* 3 */
66                 "Config file error", /* 4 */
67                 "Logfile error", /* 5 */
68                 "POSIX fork failed", /* 6 */
69                 "Bad commandline parameters", /* 7 */
70                 "No ports could be bound", /* 8 */
71                 "Can't write PID file", /* 9 */
72                 "SocketEngine could not initialize", /* 10 */
73                 "Refusing to start up as root", /* 11 */
74                 "Found a <die> tag!", /* 12 */
75                 "Couldn't load module on startup", /* 13 */
76                 "Could not create windows forked process", /* 14 */
77                 "Received SIGTERM", /* 15 */
78 };
79
80 void InspIRCd::Cleanup()
81 {
82         if (Config)
83         {
84                 for (unsigned int i = 0; i < Config->ports.size(); i++)
85                 {
86                         /* This calls the constructor and closes the listening socket */
87                         delete Config->ports[i];
88                 }
89
90                 Config->ports.clear();
91         }
92
93         /* Close all client sockets, or the new process inherits them */
94         for (std::vector<User*>::const_iterator i = this->Users->local_users.begin(); i != this->Users->local_users.end(); i++)
95         {
96                 (*i)->SetWriteError("Server shutdown");
97                 (*i)->CloseSocket();
98         }
99
100         /* We do this more than once, so that any service providers get a
101          * chance to be unhooked by the modules using them, but then get
102          * a chance to be removed themsleves.
103          *
104          * XXX there may be a better way to do this with 1.2
105          */
106         for (int tries = 0; tries < 3; tries++)
107         {
108                 std::vector<std::string> module_names = Modules->GetAllModuleNames(0);
109                 for (std::vector<std::string>::iterator k = module_names.begin(); k != module_names.end(); ++k)
110                 {
111                         /* Unload all modules, so they get a chance to clean up their listeners */
112                         this->Modules->Unload(k->c_str());
113                 }
114         }
115         /* Remove core commands */
116         Parser->RemoveCommands("<core>");
117
118         /* Cleanup Server Names */
119         for(servernamelist::iterator itr = servernames.begin(); itr != servernames.end(); ++itr)
120                 delete (*itr);
121
122         /* Delete objects dynamically allocated in constructor
123          * (destructor would be more appropriate, but we're likely exiting)
124          */
125
126         // Must be deleted before modes as it decrements modelines
127         if (this->Users)
128         {
129                 delete this->Users;
130                 this->Users = 0;
131         }
132         
133         if (this->Modes)
134         {
135                 delete this->Modes;
136                 this->Modes = 0;
137         }
138
139         if (this->XLines)
140         {
141                 delete this->XLines;
142                 this->XLines = 0;
143         }
144
145         if (this->Parser)
146         {
147                 delete this->Parser;
148                 this->Parser = 0;
149
150         if (this->stats)
151         {
152                 delete this->stats;
153                 this->stats = 0;
154         }
155
156         if (this->Modules)
157         {
158                 delete this->Modules;
159                 this->Modules = 0;
160         }
161
162         if (this->BanCache)
163                 delete this->BanCache;
164                 this->BanCache = 0;
165         }
166
167         if (this->SNO)
168         {
169                 delete this->SNO;
170                 this->SNO = 0;
171         }
172
173         if (this->Config)
174         {
175                 delete this->Config;
176                 this->Config = 0;
177         }
178
179         if (this->Res)
180         {
181                 delete this->Res;
182                 this->Res = 0;
183         }
184
185         if (this->chanlist)
186         {
187                 delete chanlist;
188                 chanlist = 0;
189         }
190
191         if (this->PI)
192         {
193                 delete this->PI;
194                 this->PI = 0;
195         }
196         
197         if (this->Threads)
198         {
199                 delete this->Threads;
200                 this->Threads = 0;
201         }
202
203         /* Needs to be deleted after Res, DNS has a timer */
204         if (this->Timers)
205         {
206                 delete this->Timers;
207                 this->Timers = 0;
208         }
209
210         /* Close logging */
211         this->Logs->CloseLogs();
212
213         if (this->Logs)
214         {
215                 delete this->Logs;
216                 this->Logs = 0;
217         }
218 }
219
220 void InspIRCd::Restart(const std::string &reason)
221 {
222         /* SendError flushes each client's queue,
223          * regardless of writeability state
224          */
225         this->SendError(reason);
226
227         this->Cleanup();
228
229         /* Figure out our filename (if theyve renamed it, we're boned) */
230         std::string me;
231
232 #ifdef WINDOWS
233         char module[MAX_PATH];
234         if (GetModuleFileName(NULL, module, MAX_PATH))
235                 me = module;
236 #else
237         me = Config->MyDir + "/inspircd";
238 #endif
239
240         if (execv(me.c_str(), Config->argv) == -1)
241         {
242                 /* Will raise a SIGABRT if not trapped */
243                 throw CoreException(std::string("Failed to execv()! error: ") + strerror(errno));
244         }
245 }
246
247 void InspIRCd::ResetMaxBans()
248 {
249         for (chan_hash::const_iterator i = chanlist->begin(); i != chanlist->end(); i++)
250                 i->second->ResetMaxBans();
251 }
252
253 /** Because hash_map doesnt free its buckets when we delete items (this is a 'feature')
254  * we must occasionally rehash the hash (yes really).
255  * We do this by copying the entries from the old hash to a new hash, causing all
256  * empty buckets to be weeded out of the hash. We dont do this on a timer, as its
257  * very expensive, so instead we do it when the user types /REHASH and expects a
258  * short delay anyway.
259  */
260 void InspIRCd::RehashUsersAndChans()
261 {
262         user_hash* old_users = this->Users->clientlist;
263         user_hash* old_uuid  = this->Users->uuidlist;
264         chan_hash* old_chans = this->chanlist;
265
266         this->Users->clientlist = new user_hash();
267         this->Users->uuidlist = new user_hash();
268         this->chanlist = new chan_hash();
269
270         for (user_hash::const_iterator n = old_users->begin(); n != old_users->end(); n++)
271                 this->Users->clientlist->insert(*n);
272
273         delete old_users;
274
275         for (user_hash::const_iterator n = old_uuid->begin(); n != old_uuid->end(); n++)
276                 this->Users->uuidlist->insert(*n);
277
278         delete old_uuid;
279
280         for (chan_hash::const_iterator n = old_chans->begin(); n != old_chans->end(); n++)
281                 this->chanlist->insert(*n);
282
283         delete old_chans;
284 }
285
286 void InspIRCd::SetSignals()
287 {
288 #ifndef WIN32
289         signal(SIGALRM, SIG_IGN);
290         signal(SIGHUP, InspIRCd::SetSignal);
291         signal(SIGPIPE, SIG_IGN);
292         signal(SIGCHLD, SIG_IGN);
293 #endif
294         signal(SIGTERM, InspIRCd::SetSignal);
295 }
296
297 void InspIRCd::QuickExit(int status)
298 {
299         exit(0);
300 }
301
302 bool InspIRCd::DaemonSeed()
303 {
304 #ifdef WINDOWS
305         printf_c("InspIRCd Process ID: \033[1;32m%lu\033[0m\n", GetCurrentProcessId());
306         return true;
307 #else
308         signal(SIGTERM, InspIRCd::QuickExit);
309
310         int childpid;
311         if ((childpid = fork ()) < 0)
312                 return false;
313         else if (childpid > 0)
314         {
315                 /* We wait here for the child process to kill us,
316                  * so that the shell prompt doesnt come back over
317                  * the output.
318                  * Sending a kill with a signal of 0 just checks
319                  * if the child pid is still around. If theyre not,
320                  * they threw an error and we should give up.
321                  */
322                 while (kill(childpid, 0) != -1)
323                         sleep(1);
324                 exit(0);
325         }
326         setsid ();
327         umask (007);
328         printf("InspIRCd Process ID: \033[1;32m%lu\033[0m\n",(unsigned long)getpid());
329
330         signal(SIGTERM, InspIRCd::SetSignal);
331
332         rlimit rl;
333         if (getrlimit(RLIMIT_CORE, &rl) == -1)
334         {
335                 this->Logs->Log("STARTUP",DEFAULT,"Failed to getrlimit()!");
336                 return false;
337         }
338         else
339         {
340                 rl.rlim_cur = rl.rlim_max;
341                 if (setrlimit(RLIMIT_CORE, &rl) == -1)
342                         this->Logs->Log("STARTUP",DEFAULT,"setrlimit() failed, cannot increase coredump size.");
343         }
344
345         return true;
346 #endif
347 }
348
349 void InspIRCd::WritePID(const std::string &filename)
350 {
351         std::string fname = (filename.empty() ? "inspircd.pid" : filename);
352         if (*(fname.begin()) != '/')
353         {
354                 std::string::size_type pos;
355                 std::string confpath = this->ConfigFileName;
356                 if ((pos = confpath.rfind("/")) != std::string::npos)
357                 {
358                         /* Leaves us with just the path */
359                         fname = confpath.substr(0, pos) + std::string("/") + fname;
360                 }
361         }
362         std::ofstream outfile(fname.c_str());
363         if (outfile.is_open())
364         {
365                 outfile << getpid();
366                 outfile.close();
367         }
368         else
369         {
370                 printf("Failed to write PID-file '%s', exiting.\n",fname.c_str());
371                 this->Logs->Log("STARTUP",DEFAULT,"Failed to write PID-file '%s', exiting.",fname.c_str());
372                 Exit(EXIT_STATUS_PID);
373         }
374 }
375
376 InspIRCd::InspIRCd(int argc, char** argv)
377         : GlobalCulls(this),
378
379          /* Functor initialisation. Note that the ordering here is very important. 
380           *
381           * THIS MUST MATCH ORDER OF DECLARATION OF THE HandleWhateverFunc classes
382           * within class InspIRCd.
383           */
384          HandleProcessUser(this),
385          HandleIsNick(this),
386          HandleIsIdent(this),
387          HandleFindDescriptor(this),
388          HandleFloodQuitUser(this),
389          HandleIsChannel(this),
390          HandleIsSID(this),
391          HandleRehash(this),
392
393          /* Functor pointer initialisation. Must match the order of the list above
394           *
395           * THIS MUST MATCH THE ORDER OF DECLARATION OF THE FUNCTORS, e.g. the methods
396           * themselves within the class.
397           */
398          ProcessUser(&HandleProcessUser),
399          IsChannel(&HandleIsChannel),
400          IsSID(&HandleIsSID),
401          Rehash(&HandleRehash),
402          IsNick(&HandleIsNick),
403          IsIdent(&HandleIsIdent),
404          FindDescriptor(&HandleFindDescriptor),
405          FloodQuitUser(&HandleFloodQuitUser)
406
407 {
408 #ifdef WIN32
409         // Strict, frequent checking of memory on debug builds
410         _CrtSetDbgFlag ( _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
411         
412         // Avoid erroneous frees on early exit
413         WindowsIPC = 0;
414 #endif
415         int found_ports = 0;
416         FailedPortList pl;
417         int do_version = 0, do_nofork = 0, do_debug = 0,
418             do_nolog = 0, do_root = 0, do_testsuite = 0;    /* flag variables */
419         char c = 0;
420
421         // Initialize so that if we exit before proper initialization they're not deleted
422         this->Logs = 0;
423         this->Threads = 0;
424         this->PI = 0;
425         this->Users = 0;
426         this->chanlist = 0;
427         this->Config = 0;
428         this->SNO = 0;
429         this->BanCache = 0;
430         this->Modules = 0;
431         this->stats = 0;
432         this->Timers = 0;
433         this->Parser = 0;
434         this->XLines = 0;
435         this->Modes = 0;
436         this->Res = 0;
437
438
439         memset(&server, 0, sizeof(server));
440         memset(&client, 0, sizeof(client));
441
442         // This must be created first, so other parts of Insp can use it while starting up
443         this->Logs = new LogManager(this);
444
445         SocketEngineFactory* SEF = new SocketEngineFactory();
446         SE = SEF->Create(this);
447         delete SEF;
448
449         ThreadEngineFactory* tef = new ThreadEngineFactory();
450         this->Threads = tef->Create(this);
451         delete tef;
452
453         /* Default implementation does nothing */
454         this->PI = new ProtocolInterface(this);
455
456         this->s_signal = 0;
457         
458         // Create base manager classes early, so nothing breaks
459         this->Users = new UserManager(this);
460         
461         this->Users->unregistered_count = 0;
462
463         this->Users->clientlist = new user_hash();
464         this->Users->uuidlist = new user_hash();
465         this->chanlist = new chan_hash();
466
467         this->Config = new ServerConfig(this);
468         this->SNO = new SnomaskManager(this);
469         this->BanCache = new BanCacheManager(this);
470         this->Modules = new ModuleManager(this);
471         this->stats = new serverstats();
472         this->Timers = new TimerManager(this);
473         this->Parser = new CommandParser(this);
474         this->XLines = new XLineManager(this);
475
476         this->Config->argv = argv;
477         this->Config->argc = argc;
478
479         if (chdir(Config->GetFullProgDir().c_str()))
480         {
481                 printf("Unable to change to my directory: %s\nAborted.", strerror(errno));
482                 exit(0);
483         }
484
485         this->Config->opertypes.clear();
486         this->Config->operclass.clear();
487
488         this->TIME = this->OLDTIME = this->startup_time = time(NULL);
489         srand(this->TIME);
490
491         *this->LogFileName = 0;
492         strlcpy(this->ConfigFileName, CONFIG_FILE, MAXBUF);
493
494         struct option longopts[] =
495         {
496                 { "nofork",     no_argument,            &do_nofork,     1       },
497                 { "logfile",    required_argument,      NULL,           'f'     },
498                 { "config",     required_argument,      NULL,           'c'     },
499                 { "debug",      no_argument,            &do_debug,      1       },
500                 { "nolog",      no_argument,            &do_nolog,      1       },
501                 { "runasroot",  no_argument,            &do_root,       1       },
502                 { "version",    no_argument,            &do_version,    1       },
503                 { "testsuite",  no_argument,            &do_testsuite,  1       },
504                 { 0, 0, 0, 0 }
505         };
506
507         while ((c = getopt_long_only(argc, argv, ":f:", longopts, NULL)) != -1)
508         {
509                 switch (c)
510                 {
511                         case 'f':
512                                 /* Log filename was set */
513                                 strlcpy(LogFileName, optarg, MAXBUF);
514                         break;
515                         case 'c':
516                                 /* Config filename was set */
517                                 strlcpy(ConfigFileName, optarg, MAXBUF);
518                         break;
519                         case 0:
520                                 /* getopt_long_only() set an int variable, just keep going */
521                         break;
522                         default:
523                                 /* Unknown parameter! DANGER, INTRUDER.... err.... yeah. */
524                                 printf("Usage: %s [--nofork] [--nolog] [--debug] [--logfile <filename>]\n\
525                                                   [--runasroot] [--version] [--config <config>] [--testsuite]\n", argv[0]);
526                                 Exit(EXIT_STATUS_ARGV);
527                         break;
528                 }
529         }
530
531         if (do_testsuite)
532                 do_nofork = do_debug = true;
533
534         if (do_version)
535         {
536                 printf("\n%s r%s\n", VERSION, REVISION);
537                 Exit(EXIT_STATUS_NOERROR);
538         }
539
540 #ifdef WIN32
541
542         // Handle forking
543         if(!do_nofork)
544         {
545                 DWORD ExitCode = WindowsForkStart(this);
546                 if(ExitCode)
547                         exit(ExitCode);
548         }
549
550         // Set up winsock
551         WSADATA wsadata;
552         WSAStartup(MAKEWORD(2,0), &wsadata);
553         ChangeWindowsSpecificPointers(this);
554 #endif
555         strlcpy(Config->MyExecutable,argv[0],MAXBUF);
556
557         /* Set the finished argument values */
558         Config->nofork = do_nofork;
559         Config->forcedebug = do_debug;
560         Config->writelog = !do_nolog;
561         Config->TestSuite = do_testsuite;
562
563         if (!this->OpenLog(argv, argc))
564         {
565                 printf("ERROR: Could not open logfile %s: %s\n\n", Config->logpath.c_str(), strerror(errno));
566                 Exit(EXIT_STATUS_LOG);
567         }
568
569         if (!ServerConfig::FileExists(this->ConfigFileName))
570         {
571                 printf("ERROR: Cannot open config file: %s\nExiting...\n", this->ConfigFileName);
572                 this->Logs->Log("STARTUP",DEFAULT,"Unable to open config file %s", this->ConfigFileName);
573                 Exit(EXIT_STATUS_CONFIG);
574         }
575
576         printf_c("\033[1;32mInspire Internet Relay Chat Server, compiled %s at %s\n",__DATE__,__TIME__);
577         printf_c("(C) InspIRCd Development Team.\033[0m\n\n");
578         printf_c("Developers:\n");
579         printf_c("\t\033[1;32mBrain, FrostyCoolSlug, w00t, Om, Special\n");
580         printf_c("\t\033[1;32mpippijn, peavey, aquanight, fez\033[0m\n\n");
581         printf_c("Others:\t\t\t\033[1;32mSee /INFO Output\033[0m\n");
582
583         Config->ClearStack();
584
585         this->Modes = new ModeParser(this);
586
587         if (!do_root)
588                 this->CheckRoot();
589         else
590         {
591                 printf("* WARNING * WARNING * WARNING * WARNING * WARNING * \n\n");
592                 printf("YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED\n");
593                 printf("AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED\n");
594                 printf("OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR\n");
595                 printf("SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN\n");
596                 printf("TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART\n");
597                 printf("THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!\n");
598                 printf("\nInspIRCd starting in 20 seconds, ctrl+c to abort...\n");
599                 sleep(20);
600         }
601
602         this->SetSignals();
603
604         if (!Config->nofork)
605         {
606                 if (!this->DaemonSeed())
607                 {
608                         printf("ERROR: could not go into daemon mode. Shutting down.\n");
609                         Logs->Log("STARTUP", DEFAULT, "ERROR: could not go into daemon mode. Shutting down.");
610                         Exit(EXIT_STATUS_FORK);
611                 }
612         }
613
614         SE->RecoverFromFork();
615
616         /* During startup we don't actually initialize this
617          * in the thread engine.
618          */
619         this->ConfigThread = new ConfigReaderThread(this, true, NULL);
620         ConfigThread->Run();
621         delete ConfigThread;
622         this->ConfigThread = NULL;
623
624         this->Res = new DNS(this);
625
626         this->AddServerName(Config->ServerName);
627
628         /*
629          * Initialise SID/UID.
630          * For an explanation as to exactly how this works, and why it works this way, see GetUID().
631          *   -- w00t
632          */
633         if (!*Config->sid)
634         {
635                 // Generate one
636                 size_t sid = 0;
637
638                 for (const char* x = Config->ServerName; *x; ++x)
639                         sid = 5 * sid + *x;
640                 for (const char* y = Config->ServerDesc; *y; ++y)
641                         sid = 5 * sid + *y;
642                 sid = sid % 999;
643
644                 Config->sid[0] = (char)(sid / 100 + 48);
645                 Config->sid[1] = (char)(((sid / 10) % 10) + 48);
646                 Config->sid[2] = (char)(sid % 10 + 48);
647                 Config->sid[3] = '\0';
648         }
649
650         /* set up fake client again this time with the correct uid */
651         this->FakeClient = new User(this, "#INVALID");
652         this->FakeClient->SetFd(FD_MAGIC_NUMBER);
653
654         // Get XLine to do it's thing.
655         this->XLines->CheckELines();
656         this->XLines->ApplyLines();
657
658         CheckDie();
659         int bounditems = BindPorts(true, found_ports, pl);
660
661         printf("\n");
662
663         this->Modules->LoadAll();
664         
665         /* Just in case no modules were loaded - fix for bug #101 */
666         this->BuildISupport();
667         InitializeDisabledCommands(Config->DisabledCommands, this);
668
669         /*if ((Config->ports.size() == 0) && (found_ports > 0))
670         {
671                 printf("\nERROR: I couldn't bind any ports! Are you sure you didn't start InspIRCd twice?\n");
672                 Logs->Log("STARTUP", DEFAULT,"ERROR: I couldn't bind any ports! Something else is bound to those ports!");
673                 Exit(EXIT_STATUS_BIND);
674         }*/
675
676         if (Config->ports.size() != (unsigned int)found_ports)
677         {
678                 printf("\nWARNING: Not all your client ports could be bound --\nstarting anyway with %d of %d client ports bound.\n\n", bounditems, found_ports);
679                 printf("The following port(s) failed to bind:\n");
680                 printf("Hint: Try using an IP instead of blank or *\n\n");
681                 int j = 1;
682                 for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
683                 {
684                         printf("%d.\tIP: %s\tPort: %lu\n", j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second);
685                 }
686         }
687
688         printf("\nInspIRCd is now running as '%s'[%s] with %d max open sockets\n", Config->ServerName,Config->GetSID().c_str(), SE->GetMaxFds());
689         
690 #ifndef WINDOWS
691         if (!Config->nofork)
692         {
693                 if (kill(getppid(), SIGTERM) == -1)
694                 {
695                         printf("Error killing parent process: %s\n",strerror(errno));
696                         Logs->Log("STARTUP", DEFAULT, "Error killing parent process: %s",strerror(errno));
697                 }
698         }
699
700         if (isatty(0) && isatty(1) && isatty(2))
701         {
702                 /* We didn't start from a TTY, we must have started from a background process -
703                  * e.g. we are restarting, or being launched by cron. Dont kill parent, and dont
704                  * close stdin/stdout
705                  */
706                 if (!do_nofork)
707                 {
708                         fclose(stdin);
709                         fclose(stderr);
710                         fclose(stdout);
711                 }
712                 else
713                 {
714                         Logs->Log("STARTUP", DEFAULT,"Keeping pseudo-tty open as we are running in the foreground.");
715                 }
716         }
717 #else
718         WindowsIPC = new IPC(this);
719         if(!Config->nofork)
720         {
721                 WindowsForkKillOwner(this);
722                 FreeConsole();
723         }
724 #endif
725
726         Logs->Log("STARTUP", DEFAULT, "Startup complete as '%s'[%s], %d max open sockets", Config->ServerName,Config->GetSID().c_str(), SE->GetMaxFds());
727
728         this->WritePID(Config->PID);
729 }
730
731 int InspIRCd::Run()
732 {
733         /* See if we're supposed to be running the test suite rather than entering the mainloop */
734         if (Config->TestSuite)
735         {
736                 TestSuite* ts = new TestSuite(this);
737                 delete ts;
738                 Exit(0);
739         }
740
741         while (true)
742         {
743 #ifndef WIN32
744                 static rusage ru;
745 #else
746                 static time_t uptime;
747                 static struct tm * stime;
748                 static char window_title[100];
749 #endif
750
751                 /* Check if there is a config thread which has finished executing but has not yet been freed */
752                 if (this->ConfigThread && this->ConfigThread->GetExitFlag())
753                 {
754                         /* Rehash has completed */
755                         this->Logs->Log("CONFIG",DEBUG,"Detected ConfigThread exiting, tidying up...");
756
757                         /* IMPORTANT: This delete may hang if you fuck up your thread syncronization.
758                          * It will hang waiting for the ConfigThread to 'join' to avoid race conditons,
759                          * until the other thread is completed.
760                          */
761                         delete ConfigThread;
762                         ConfigThread = NULL;
763
764                         /* These are currently not known to be threadsafe, so they are executed outside
765                          * of the thread. It would be pretty simple to move them to the thread Run method
766                          * once they are known threadsafe with all the correct mutexes in place.
767                          *
768                          * XXX: The order of these is IMPORTANT, do not reorder them without testing
769                          * thoroughly!!!
770                          */
771                         this->XLines->CheckELines();
772                         this->XLines->ApplyLines();
773                         this->Res->Rehash();
774                         this->ResetMaxBans();
775                         InitializeDisabledCommands(Config->DisabledCommands, this);
776                         FOREACH_MOD_I(this, I_OnRehash, OnRehash(Config->RehashUser, Config->RehashParameter));
777                         this->BuildISupport();
778                 }
779
780                 /* time() seems to be a pretty expensive syscall, so avoid calling it too much.
781                  * Once per loop iteration is pleanty.
782                  */
783                 OLDTIME = TIME;
784                 TIME = time(NULL);
785
786                 /* Run background module timers every few seconds
787                  * (the docs say modules shouldnt rely on accurate
788                  * timing using this event, so we dont have to
789                  * time this exactly).
790                  */
791                 if (TIME != OLDTIME)
792                 {
793                         if (TIME < OLDTIME)
794                         {
795                                 SNO->WriteToSnoMask('A', "\002EH?!\002 -- Time is flowing BACKWARDS in this dimension! Clock drifted backwards %lu secs.", (unsigned long)OLDTIME-TIME);
796                         }
797
798                         if ((TIME % 3600) == 0)
799                         {
800                                 this->RehashUsersAndChans();
801                                 FOREACH_MOD_I(this, I_OnGarbageCollect, OnGarbageCollect());
802                         }
803
804                         Timers->TickTimers(TIME);
805                         this->DoBackgroundUserStuff();
806
807                         if ((TIME % 5) == 0)
808                         {
809                                 FOREACH_MOD_I(this,I_OnBackgroundTimer,OnBackgroundTimer(TIME));
810                                 SNO->FlushSnotices();
811                         }
812 #ifndef WIN32
813                         /* Same change as in cmd_stats.cpp, use RUSAGE_SELF rather than '0' -- Om */
814                         if (!getrusage(RUSAGE_SELF, &ru))
815                         {
816                                 gettimeofday(&this->stats->LastSampled, NULL);
817                                 this->stats->LastCPU = ru.ru_utime;
818                         }
819 #else
820                         WindowsIPC->Check();    
821 #endif
822                 }
823
824                 /* Call the socket engine to wait on the active
825                  * file descriptors. The socket engine has everything's
826                  * descriptors in its list... dns, modules, users,
827                  * servers... so its nice and easy, just one call.
828                  * This will cause any read or write events to be
829                  * dispatched to their handlers.
830                  */
831                 this->SE->DispatchEvents();
832
833                 /* if any users were quit, take them out */
834                 this->GlobalCulls.Apply();
835
836                 /* If any inspsockets closed, remove them */
837                 this->BufferedSocketCull();
838
839                 if (this->s_signal)
840                 {
841                         this->SignalHandler(s_signal);
842                         this->s_signal = 0;
843                 }
844         }
845
846         return 0;
847 }
848
849 void InspIRCd::BufferedSocketCull()
850 {
851         for (std::map<BufferedSocket*,BufferedSocket*>::iterator x = SocketCull.begin(); x != SocketCull.end(); ++x)
852         {
853                 this->Logs->Log("MISC",DEBUG,"Cull socket");
854                 SE->DelFd(x->second);
855                 x->second->Close();
856                 delete x->second;
857         }
858         SocketCull.clear();
859 }
860
861 /**********************************************************************************/
862
863 /**
864  * An ircd in five lines! bwahahaha. ahahahahaha. ahahah *cough*.
865  */
866
867 int main(int argc, char ** argv)
868 {
869         SI = new InspIRCd(argc, argv);
870         mysig = &SI->s_signal;
871         SI->Run();
872         delete SI;
873         return 0;
874 }
875
876 /* this returns true when all modules are satisfied that the user should be allowed onto the irc server
877  * (until this returns true, a user will block in the waiting state, waiting to connect up to the
878  * registration timeout maximum seconds)
879  */
880 bool InspIRCd::AllModulesReportReady(User* user)
881 {
882         for (EventHandlerIter i = Modules->EventHandlers[I_OnCheckReady].begin(); i != Modules->EventHandlers[I_OnCheckReady].end(); ++i)
883         {
884                 if (!(*i)->OnCheckReady(user))
885                         return false;
886         }
887         return true;
888 }
889
890 time_t InspIRCd::Time()
891 {
892         return TIME;
893 }
894
895 void InspIRCd::SetSignal(int signal)
896 {
897         *mysig = signal;
898 }