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