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