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