]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd_io.cpp
Missing a changeover
[user/henk/code/inspircd.git] / src / inspircd_io.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 using namespace std;
18
19 #include "inspircd_config.h"
20 #include <sys/time.h>
21 #include <sys/resource.h>
22 #include <sys/types.h>
23 #include <string>
24 #include <unistd.h>
25 #include <sstream>
26 #include <iostream>
27 #include <fstream>
28 #include "message.h"
29 #include "inspircd.h"
30 #include "inspircd_io.h"
31 #include "inspstring.h"
32 #include "helperfuncs.h"
33 #include "userprocess.h"
34 #include "xline.h"
35
36 extern ServerConfig *Config;
37 extern InspIRCd* ServerInstance;
38 extern int openSockfd[MAXSOCKS];
39 extern time_t TIME;
40
41 extern int MODCOUNT;
42 extern std::vector<Module*> modules;
43 extern std::vector<ircd_module*> factory;
44
45 ServerConfig::ServerConfig()
46 {
47         this->ClearStack();
48         *TempDir = *ServerName = *Network = *ServerDesc = *AdminName = '\0';
49         *HideWhoisServer = *AdminEmail = *AdminNick = *diepass = *restartpass = '\0';
50         *CustomVersion = *motd = *rules = *PrefixQuit = *DieValue = *DNSServer = '\0';
51         *OperOnlyStats = *ModPath = *MyExecutable = *DisabledCommands = *PID = '\0';
52         log_file = NULL;
53         nofork = HideBans = HideSplits = unlimitcore = false;
54         AllowHalfop = true;
55         dns_timeout = DieDelay = 5;
56         MaxTargets = 20;
57         NetBufferSize = 10240;
58         SoftLimit = MAXCLIENTS;
59         MaxConn = SOMAXCONN;
60         MaxWhoResults = 100;
61         debugging = 0;
62         LogLevel = DEFAULT;
63         maxbans.clear();
64 }
65
66 void ServerConfig::ClearStack()
67 {
68         include_stack.clear();
69 }
70
71 Module* ServerConfig::GetIOHook(int port)
72 {
73         std::map<int,Module*>::iterator x = IOHookModule.find(port);
74         return (x != IOHookModule.end() ? x->second : NULL);
75 }
76
77 bool ServerConfig::AddIOHook(int port, Module* iomod)
78 {
79         if (!GetIOHook(port))
80         {
81                 IOHookModule[port] = iomod;
82                 return true;
83         }
84         else
85         {
86                 ModuleException err("Port already hooked by another module");
87                 throw(err);
88                 return false;
89         }
90 }
91
92 bool ServerConfig::DelIOHook(int port)
93 {
94         std::map<int,Module*>::iterator x = IOHookModule.find(port);
95         if (x != IOHookModule.end())
96         {
97                 IOHookModule.erase(x);
98                 return true;
99         }
100         return false;
101 }
102
103 bool ServerConfig::CheckOnce(char* tag, bool bail, userrec* user)
104 {
105         int count = ConfValueEnum(tag,&Config->config_f);
106         if (count > 1)
107         {
108                 if (bail)
109                 {
110                         printf("There were errors in your configuration:\nYou have more than one <%s> tag, this is not permitted.\n",tag);
111                         Exit(0);
112                 }
113                 else
114                 {
115                         if (user)
116                         {
117                                 WriteServ(user->fd,"There were errors in your configuration:");
118                                 WriteServ(user->fd,"You have more than one <%s> tag, this is not permitted.\n",tag);
119                         }
120                         else
121                         {
122                                 WriteOpers("There were errors in the configuration file:");
123                                 WriteOpers("You have more than one <%s> tag, this is not permitted.\n",tag);
124                         }
125                 }
126                 return false;
127         }
128         if (count < 1)
129         {
130                 if (bail)
131                 {
132                         printf("There were errors in your configuration:\nYou have not defined a <%s> tag, this is required.\n",tag);
133                         Exit(0);
134                 }
135                 else
136                 {
137                         if (user)
138                         {
139                                 WriteServ(user->fd,"There were errors in your configuration:");
140                                 WriteServ(user->fd,"You have not defined a <%s> tag, this is required.",tag);
141                         }
142                         else
143                         {
144                                 WriteOpers("There were errors in the configuration file:");
145                                 WriteOpers("You have not defined a <%s> tag, this is required.",tag);
146                         }
147                 }
148                 return false;
149         }
150         return true;
151 }
152
153 bool NoValidation(const char* tag, const char* value, void* data)
154 {
155         log(DEBUG,"No validation for <%s:%s>",tag,value);
156         return true;
157 }
158
159 bool ValidateTempDir(const char* tag, const char* value, void* data)
160 {
161         char* x = (char*)data;
162         if (!*x)
163                 strlcpy(x,"/tmp",1024);
164         return true;
165 }
166  
167 bool ValidateMaxTargets(const char* tag, const char* value, void* data)
168 {
169         int* x = (int*)data;
170         if ((*x < 0) || (*x > 31))
171         {
172                 log(DEFAULT,"WARNING: <options:maxtargets> value is greater than 31 or less than 0, set to 20.");
173                 *x = 20;
174         }
175         return true;
176 }
177
178 bool ValidateSoftLimit(const char* tag, const char* value, void* data)
179 {
180         int* x = (int*)data;    
181         if ((*x < 1) || (*x > MAXCLIENTS))
182         {
183                 log(DEFAULT,"WARNING: <options:softlimit> value is greater than %d or less than 0, set to %d.",MAXCLIENTS,MAXCLIENTS);
184                 *x = MAXCLIENTS;
185         }
186         return true;
187 }
188
189 bool ValidateMaxConn(const char* tag, const char* value, void* data)
190 {
191         int* x = (int*)data;    
192         if (*x > SOMAXCONN)
193                 log(DEFAULT,"WARNING: <options:somaxconn> value may be higher than the system-defined SOMAXCONN value!");
194         if (!*x)
195                 *x = SOMAXCONN;
196         return true;
197 }
198
199 bool ValidateDnsTimeout(const char* tag, const char* value, void* data)
200 {
201         int* x = (int*)data;
202         if (!*x)
203                 *x = 5;
204         return true;
205 }
206
207 bool ValidateDnsServer(const char* tag, const char* value, void* data)
208 {
209         char* x = (char*)data;
210         if (!*x)
211         {
212                 // attempt to look up their nameserver from /etc/resolv.conf
213                 log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
214                 ifstream resolv("/etc/resolv.conf");
215                 std::string nameserver;
216                 bool found_server = false;
217
218                 if (resolv.is_open())
219                 {
220                         while (resolv >> nameserver)
221                         {
222                                 if ((nameserver == "nameserver") && (!found_server))
223                                 {
224                                         resolv >> nameserver;
225                                         strlcpy(x,nameserver.c_str(),MAXBUF);
226                                         found_server = true;
227                                         log(DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",nameserver.c_str());
228                                 }
229                         }
230
231                         if (!found_server)
232                         {
233                                 log(DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
234                                 strlcpy(x,"127.0.0.1",MAXBUF);
235                         }
236                 }
237                 else
238                 {
239                         log(DEFAULT,"/etc/resolv.conf can't be opened! Defaulting to nameserver '127.0.0.1'!");
240                         strlcpy(x,"127.0.0.1",MAXBUF);
241                 }
242         }
243         return true;
244 }
245
246 bool ValidateModPath(const char* tag, const char* value, void* data)
247 {
248         char* x = (char*)data;  
249         if (!*x)
250                 strlcpy(x,MOD_PATH,MAXBUF);
251         return true;
252 }
253
254
255 bool ValidateServerName(const char* tag, const char* value, void* data)
256 {
257         char* x = (char*)data;
258         if (!strchr(x,'.'))
259         {
260                 log(DEFAULT,"WARNING: <server:name> '%s' is not a fully-qualified domain name. Changed to '%s%c'",x,x,'.');
261                 charlcat(x,'.',MAXBUF);
262         }
263         strlower(x);
264         return true;
265 }
266
267 bool ValidateNetBufferSize(const char* tag, const char* value, void* data)
268 {
269         if ((!Config->NetBufferSize) || (Config->NetBufferSize > 65535) || (Config->NetBufferSize < 1024))
270         {
271                 log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");
272                 Config->NetBufferSize = 10240;
273         }
274         return true;
275 }
276
277 bool ValidateMaxWho(const char* tag, const char* value, void* data)
278 {
279         if ((!Config->MaxWhoResults) || (Config->MaxWhoResults > 65535) || (Config->MaxWhoResults < 1))
280         {
281                 log(DEFAULT,"No MaxWhoResults specified or size out of range, setting to default of 128.");
282                 Config->MaxWhoResults = 128;
283         }
284         return true;
285 }
286
287 bool ValidateLogLevel(const char* tag, const char* value, void* data)
288 {
289         const char* dbg = (const char*)data;
290         Config->LogLevel = DEFAULT;
291         if (!strcmp(dbg,"debug"))
292         {
293                 Config->LogLevel = DEBUG;
294                 Config->debugging = 1;
295         }
296         else if (!strcmp(dbg,"verbose"))
297                 Config->LogLevel = VERBOSE;
298         else if (!strcmp(dbg,"default"))
299                 Config->LogLevel = DEFAULT;
300         else if (!strcmp(dbg,"sparse"))
301                 Config->LogLevel = SPARSE;
302         else if (!strcmp(dbg,"none"))
303                 Config->LogLevel = NONE;
304         return true;
305 }
306
307 bool ValidateMotd(const char* tag, const char* value, void* data)
308 {
309         readfile(Config->MOTD,Config->motd);
310         return true;
311 }
312
313 bool ValidateRules(const char* tag, const char* value, void* data)
314 {
315         readfile(Config->RULES,Config->rules);
316         return true;
317 }
318
319
320 void ServerConfig::Read(bool bail, userrec* user)
321 {
322         char debug[MAXBUF];
323         char CM1[MAXBUF],CM2[MAXBUF];
324         char ServName[MAXBUF],Value[MAXBUF];
325         char dataline[1024];
326         ConnectClass c;
327         std::stringstream errstr;
328
329         static InitialConfig Values[] = {
330                 {"options",     "softlimit",            &this->SoftLimit,               DT_INTEGER, ValidateSoftLimit},
331                 {"options",     "somaxconn",            &this->MaxConn,                 DT_INTEGER, ValidateMaxConn},
332                 {"server",      "name",                 &this->ServerName,              DT_CHARPTR, ValidateServerName},
333                 {"server",      "description",          &this->ServerDesc,              DT_CHARPTR, NoValidation},
334                 {"server",      "network",              &this->Network,                 DT_CHARPTR, NoValidation},
335                 {"admin",       "name",                 &this->AdminName,               DT_CHARPTR, NoValidation},
336                 {"admin",       "email",                &this->AdminEmail,              DT_CHARPTR, NoValidation},
337                 {"admin",       "nick",                 &this->AdminNick,               DT_CHARPTR, NoValidation},
338                 {"files",       "motd",                 &this->motd,                    DT_CHARPTR, ValidateMotd},
339                 {"files",       "rules",                &this->rules,                   DT_CHARPTR, ValidateRules},
340                 {"power",       "diepass",              &this->diepass,                 DT_CHARPTR, NoValidation},      
341                 {"power",       "pauseval",             &this->DieDelay,                DT_INTEGER, NoValidation},
342                 {"power",       "restartpass",          &this->restartpass,             DT_CHARPTR, NoValidation},
343                 {"options",     "prefixquit",           &this->PrefixQuit,              DT_CHARPTR, NoValidation},
344                 {"die",         "value",                &this->DieValue,                DT_CHARPTR, NoValidation},
345                 {"options",     "loglevel",             &debug,                         DT_CHARPTR, ValidateLogLevel},
346                 {"options",     "netbuffersize",        &this->NetBufferSize,           DT_INTEGER, ValidateNetBufferSize},
347                 {"options",     "maxwho",               &this->MaxWhoResults,           DT_INTEGER, ValidateMaxWho},
348                 {"options",     "allowhalfop",          &this->AllowHalfop,             DT_BOOLEAN, NoValidation},
349                 {"dns",         "server",               &this->DNSServer,               DT_CHARPTR, ValidateDnsServer},
350                 {"dns",         "timeout",              &this->dns_timeout,             DT_INTEGER, ValidateDnsTimeout},
351                 {"options",     "moduledir",            &this->ModPath,                 DT_CHARPTR, ValidateModPath},
352                 {"disabled",    "commands",             &this->DisabledCommands,        DT_CHARPTR, NoValidation},
353                 {"options",     "operonlystats",        &this->OperOnlyStats,           DT_CHARPTR, NoValidation},
354                 {"options",     "customversion",        &this->CustomVersion,           DT_CHARPTR, NoValidation},
355                 {"options",     "hidesplits",           &this->HideSplits,              DT_BOOLEAN, NoValidation},
356                 {"options",     "hidebans",             &this->HideBans,                DT_BOOLEAN, NoValidation},
357                 {"options",     "hidewhois",            &this->HideWhoisServer,         DT_CHARPTR, NoValidation},
358                 {"options",     "tempdir",              &this->TempDir,                 DT_CHARPTR, ValidateTempDir},
359                 {NULL}
360         };
361         
362         include_stack.clear();
363
364         if (!LoadConf(CONFIG_FILE,&Config->config_f,&errstr))
365         {
366                 errstr.seekg(0);
367                 log(DEFAULT,"There were errors in your configuration:\n%s",errstr.str().c_str());
368
369                 if (bail)
370                 {
371                         printf("There were errors in your configuration:\n%s",errstr.str().c_str());
372                         Exit(0);
373                 }
374                 else
375                 {
376                         if (user)
377                         {
378                                 WriteServ(user->fd,"NOTICE %s :There were errors in the configuration file:",user->nick);
379                                 while (!errstr.eof())
380                                 {
381                                         errstr.getline(dataline,1024);
382                                         WriteServ(user->fd,"NOTICE %s :%s",user->nick,dataline);
383                                 }
384                         }
385                         else
386                         {
387                                 WriteOpers("There were errors in the configuration file:");
388                                 while (!errstr.eof())
389                                 {
390                                         errstr.getline(dataline,1024);
391                                         WriteOpers(dataline);
392                                 }
393                         }
394
395                         return;
396                 }
397         }
398
399         /* Check we dont have more than one of singular tags
400          */
401         if (!CheckOnce("server",bail,user) || !CheckOnce("admin",bail,user) || !CheckOnce("files",bail,user)
402                 || !CheckOnce("power",bail,user) || !CheckOnce("options",bail,user) || !CheckOnce("pid",bail,user))
403         {
404                 return;
405         }
406
407         char* convert;
408         for (int Index = 0; Values[Index].tag; Index++)
409         {
410                 int* val_i = (int*) Values[Index].val;
411                 char* val_c = (char*) Values[Index].val;
412
413                 switch (Values[Index].datatype)
414                 {
415                         case DT_CHARPTR:
416                                 ConfValue(Values[Index].tag, Values[Index].value, 0, val_c, &this->config_f);
417                         break;
418
419                         case DT_INTEGER:
420                                 convert = new char[MAXBUF];
421                                 ConfValue(Values[Index].tag, Values[Index].value, 0, convert, &this->config_f);
422                                 *val_i = atoi(convert);
423                                 delete[] convert;
424                         break;
425
426                         case DT_BOOLEAN:
427                                 convert = new char[MAXBUF];
428                                 ConfValue(Values[Index].tag, Values[Index].value, 0, convert, &this->config_f);
429                                 *val_i = ((*convert == tolower('y')) || (*convert == tolower('t')) || (*convert == '1'));
430                                 delete[] convert;
431                         break;
432
433                         case DT_NOTHING:
434                         break;
435                 }
436
437                 Values[Index].validation_function(Values[Index].tag, Values[Index].value, Values[Index].val);
438         }
439
440         log(DEFAULT,"Reading connect classes...");
441         Classes.clear();
442
443         for (int i = 0; i < ConfValueEnum("connect",&Config->config_f); i++)
444         {
445                 *Value = 0;
446                 ConfValue("connect","allow",i,Value,&Config->config_f);
447                 if (*Value)
448                 {
449                         c.host = Value;
450                         c.type = CC_ALLOW;
451                         *Value = 0;
452                         ConfValue("connect","password",i,Value,&Config->config_f);
453                         c.pass = Value;
454                         c.registration_timeout = ConfValueInteger("connect","timeout",i,&Config->config_f); // default is 2 minutes
455                         c.pingtime = ConfValueInteger("connect","pingfreq",i,&Config->config_f);
456                         c.flood = ConfValueInteger("connect","flood",i,&Config->config_f);
457                         c.threshold = ConfValueInteger("connect","threshold",i,&Config->config_f);
458                         c.sendqmax = ConfValueInteger("connect","sendq",i,&Config->config_f);
459                         c.recvqmax = ConfValueInteger("connect","recvq",i,&Config->config_f);
460                         c.maxlocal = ConfValueInteger("connect","localmax",i,&Config->config_f);
461                         c.maxglobal = ConfValueInteger("connect","globalmax",i,&Config->config_f);
462
463                         
464                         if (c.maxlocal == 0)
465                         {
466                                 c.maxlocal = 3;
467                         }
468
469                         if (c.maxglobal == 0)
470                         {
471                                 c.maxglobal = 3;
472                         }
473
474                         if (c.threshold == 0)
475                         {
476                                 c.threshold = 1;
477                                 c.flood = 999;
478                                 log(DEFAULT,"Warning: Connect allow line '%s' has no flood/threshold settings. Setting this tag to 999 lines in 1 second.",c.host.c_str());
479                         }
480
481                         if (c.sendqmax == 0)
482                         {
483                                 c.sendqmax = 262114;
484                         }
485                         if (c.recvqmax == 0)
486                         {
487                                 c.recvqmax = 4096;
488                         }
489                         if (c.registration_timeout == 0)
490                         {
491                                 c.registration_timeout = 90;
492                         }
493                         if (c.pingtime == 0)
494                         {
495                                 c.pingtime = 120;
496                         }
497
498                         Classes.push_back(c);
499                 }
500                 else
501                 {
502                         ConfValue("connect","deny",i,Value,&Config->config_f);
503                         c.host = Value;
504                         c.type = CC_DENY;
505                         Classes.push_back(c);
506                         log(DEBUG,"Read connect class type DENY, host=%s",c.host.c_str());
507                 }
508         }
509
510         Config->ulines.clear();
511
512         for (int i = 0; i < ConfValueEnum("uline",&Config->config_f); i++)
513         {   
514                 ConfValue("uline","server",i,ServName,&Config->config_f);
515                 {
516                         log(DEBUG,"Read ULINE '%s'",ServName);
517                         Config->ulines.push_back(ServName);
518                 }
519         }
520
521         maxbans.clear();
522
523         for (int count = 0; count < Config->ConfValueEnum("banlist",&Config->config_f); count++)
524         {
525                 Config->ConfValue("banlist","chan",count,CM1,&Config->config_f);
526                 Config->ConfValue("banlist","limit",count,CM2,&Config->config_f);
527                 maxbans[CM1] = atoi(CM2);
528         }
529
530         ReadClassesAndTypes();
531         log(DEFAULT,"Reading K lines,Q lines and Z lines from config...");
532         read_xline_defaults();
533         log(DEFAULT,"Applying K lines, Q lines and Z lines...");
534         apply_lines(APPLY_ALL);
535
536         ConfValue("pid","file",0,Config->PID,&Config->config_f);
537         // write once here, to try it out and make sure its ok
538         WritePID(Config->PID);
539
540         log(DEFAULT,"Done reading configuration file, InspIRCd is now starting.");
541         if (!bail)
542         {
543                 log(DEFAULT,"Adding and removing modules due to rehash...");
544
545                 std::vector<std::string> old_module_names, new_module_names, added_modules, removed_modules;
546
547                 // store the old module names
548                 for (std::vector<std::string>::iterator t = module_names.begin(); t != module_names.end(); t++)
549                 {
550                         old_module_names.push_back(*t);
551                 }
552
553                 // get the new module names
554                 for (int count2 = 0; count2 < ConfValueEnum("module",&Config->config_f); count2++)
555                 {
556                         ConfValue("module","name",count2,Value,&Config->config_f);
557                         new_module_names.push_back(Value);
558                 }
559
560                 // now create a list of new modules that are due to be loaded
561                 // and a seperate list of modules which are due to be unloaded
562                 for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++)
563                 {
564                         bool added = true;
565
566                         for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++)
567                         {
568                                 if (*old == *_new)
569                                         added = false;
570                         }
571
572                         if (added)
573                                 added_modules.push_back(*_new);
574                 }
575
576                 for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++)
577                 {
578                         bool removed = true;
579                         for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++)
580                         {
581                                 if (*newm == *oldm)
582                                         removed = false;
583                         }
584
585                         if (removed)
586                                 removed_modules.push_back(*oldm);
587                 }
588
589                 /*
590                  * now we have added_modules, a vector of modules to be loaded,
591                  * and removed_modules, a vector of modules
592                  * to be removed.
593                  */
594                 int rem = 0, add = 0;
595                 if (!removed_modules.empty())
596                         for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
597                         {
598                                 if (ServerInstance->UnloadModule(removing->c_str()))
599                                 {
600                                         WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str());
601
602                                         if (user)
603                                                 WriteServ(user->fd,"973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());
604
605                                         rem++;
606                                 }
607                                 else
608                                 {
609                                         if (user)
610                                                 WriteServ(user->fd,"972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ServerInstance->ModuleError());
611                                 }
612                         }
613
614                 if (!added_modules.empty())
615                 for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
616                 {
617                         if (ServerInstance->LoadModule(adding->c_str()))
618                         {
619                                 WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());
620
621                                 if (user)
622                                         WriteServ(user->fd,"975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());
623
624                                 add++;
625                         }
626                         else
627                         {
628                                 if (user)
629                                         WriteServ(user->fd,"974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ServerInstance->ModuleError());
630                         }
631                 }
632
633                 log(DEFAULT,"Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),(unsigned long)add,(unsigned long)added_modules.size());
634         }
635 }
636
637
638 void Exit (int status)
639 {
640         if (Config->log_file)
641                 fclose(Config->log_file);
642         send_error("Server shutdown.");
643         exit (status);
644 }
645
646 void Killed(int status)
647 {
648         if (Config->log_file)
649                 fclose(Config->log_file);
650         send_error("Server terminated.");
651         exit(status);
652 }
653
654 char* CleanFilename(char* name)
655 {
656         char* p = name + strlen(name);
657         while ((p != name) && (*p != '/')) p--;
658         return (p != name ? ++p : p);
659 }
660
661
662 void Rehash(int status)
663 {
664         WriteOpers("Rehashing config file %s due to SIGHUP",CleanFilename(CONFIG_FILE));
665         fclose(Config->log_file);
666         OpenLog(NULL,0);
667         Config->Read(false,NULL);
668         FOREACH_MOD(I_OnRehash,OnRehash(""));
669 }
670
671
672
673 void Start (void)
674 {
675         printf("\033[1;32mInspire Internet Relay Chat Server, compiled %s at %s\n",__DATE__,__TIME__);
676         printf("(C) ChatSpike Development team.\033[0m\n\n");
677         printf("Developers:\t\t\033[1;32mBrain, FrostyCoolSlug, w00t, Om\033[0m\n");
678         printf("Others:\t\t\t\033[1;32mSee /INFO Output\033[0m\n");
679         printf("Name concept:\t\t\033[1;32mLord_Zathras\033[0m\n\n");
680 }
681
682 void WritePID(std::string filename)
683 {
684         ofstream outfile(filename.c_str());
685         if (outfile.is_open())
686         {
687                 outfile << getpid();
688                 outfile.close();
689         }
690         else
691         {
692                 printf("Failed to write PID-file '%s', exiting.\n",filename.c_str());
693                 log(DEFAULT,"Failed to write PID-file '%s', exiting.",filename.c_str());
694                 Exit(0);
695         }
696 }
697
698 void SetSignals()
699 {
700         signal (SIGALRM, SIG_IGN);
701         signal (SIGHUP, Rehash);
702         signal (SIGPIPE, SIG_IGN);
703         signal (SIGTERM, Exit);
704         signal (SIGSEGV, Error);
705 }
706
707
708 int DaemonSeed (void)
709 {
710         int childpid;
711         if ((childpid = fork ()) < 0)
712                 return (ERROR);
713         else if (childpid > 0)
714         {
715                 /* We wait a few seconds here, so that the shell prompt doesnt come back over the output */
716                 sleep(6);
717                 exit (0);
718         }
719         setsid ();
720         umask (007);
721         printf("InspIRCd Process ID: \033[1;32m%lu\033[0m\n",(unsigned long)getpid());
722
723         if (Config->unlimitcore)
724         {
725                 rlimit rl;
726                 if (getrlimit(RLIMIT_CORE, &rl) == -1)
727                 {
728                         log(DEFAULT,"Failed to getrlimit()!");
729                         return(FALSE);
730                 }
731                 else
732                 {
733                         rl.rlim_cur = rl.rlim_max;
734                         if (setrlimit(RLIMIT_CORE, &rl) == -1)
735                                 log(DEFAULT,"setrlimit() failed, cannot increase coredump size.");
736                 }
737         }
738   
739         return (TRUE);
740 }
741
742
743 /* Make Sure Modules Are Avaliable!
744  * (BugFix By Craig.. See? I do work! :p)
745  * Modified by brain, requires const char*
746  * to work with other API functions
747  */
748
749 bool FileExists (const char* file)
750 {
751         FILE *input;
752         if ((input = fopen (file, "r")) == NULL)
753         {
754                 return(false);
755         }
756         else
757         {
758                 fclose (input);
759                 return(true);
760         }
761 }
762
763 /* ConfProcess does the following things to a config line in the following order:
764  *
765  * Processes the line for syntax errors as shown below
766  *  (1) Line void of quotes or equals (a malformed, illegal tag format)
767  *  (2) Odd number of quotes on the line indicating a missing quote
768  *  (3) number of equals signs not equal to number of quotes / 2 (missing an equals sign)
769  *  (4) Spaces between the opening bracket (<) and the keyword
770  *  (5) Spaces between a keyword and an equals sign
771  *  (6) Spaces between an equals sign and a quote
772  * Removes trailing spaces
773  * Removes leading spaces
774  * Converts tabs to spaces
775  * Turns multiple spaces that are outside of quotes into single spaces
776  */
777
778 std::string ServerConfig::ConfProcess(char* buffer, long linenumber, std::stringstream* errorstream, bool &error, std::string filename)
779 {
780         long number_of_quotes = 0;
781         long number_of_equals = 0;
782         bool has_open_bracket = false;
783         bool in_quotes = false;
784         char* trailing;
785
786         error = false;
787         if (!buffer)
788         {
789                 return "";
790         }
791         // firstly clean up the line by stripping spaces from the start and end and converting tabs to spaces
792         for (char* d = buffer; *d; d++)
793                 if (*d == 9)
794                         *d = ' ';
795         while (*buffer == ' ') buffer++;
796         trailing = buffer + strlen(buffer) - 1;
797         while (*trailing == ' ') *trailing-- = '\0';
798
799         // empty lines are syntactically valid, as are comments
800         if (!(*buffer) || buffer[0] == '#')
801                 return "";
802
803         for (char* c = buffer; *c; c++)
804         {
805                 // convert all spaces that are OUTSIDE quotes into hardspace (0xA0) as this will make them easier to
806                 // search and replace later :)
807                 if ((!in_quotes) && (*c == ' '))
808                         *c = '\xA0';
809                 if ((*c == '<') && (!in_quotes))
810                 {
811                         has_open_bracket = true;
812                         if (!(*(buffer+1)))
813                         {
814                                 *errorstream << "Tag without identifier at " << filename << ":" << linenumber << endl;
815                                 error = true;
816                                 return "";
817                         }
818                         else if ((tolower(*(c+1)) < 'a') || (tolower(*(c+1)) > 'z'))
819                         {
820                                 *errorstream << "Invalid characters in identifier at " << filename << ":" << linenumber << endl;
821                                 error = true;
822                                 return "";
823                         }
824                 }
825                 if (*c == '"')
826                 {
827                         number_of_quotes++;
828                         in_quotes = (!in_quotes);
829                 }
830                 if ((*c == '=') && (!in_quotes))
831                 {
832                         number_of_equals++;
833                         if (*(c+1) == 0)
834                         {
835                                 *errorstream << "Variable without a value at " << filename << ":" << linenumber << endl;
836                                 error = true;
837                                 return "";
838                         }
839                         else if (*(c+1) != '"')
840                         {
841                                 *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl;
842                                 error = true;
843                                 return "";
844                         }
845                         else if (c == buffer)
846                         {
847                                 *errorstream << "Value without a variable (line starts with '=') at " << filename << ":" << linenumber << endl;
848                                 error = true;
849                                 return "";
850                         }
851                         else if (*(c-1) == '\xA0')
852                         {
853                                 *errorstream << "Variable name not followed immediately by its value at " << filename << ":" << linenumber << endl;
854                                 error = true;
855                                 return "";
856                         }
857                 }
858         }
859         // no quotes, and no equals. something freaky.
860         if ((!number_of_quotes) || (!number_of_equals) && (strlen(buffer)>2) && (*buffer == '<'))
861         {
862                 *errorstream << "Malformed tag at " << filename << ":" << linenumber << endl;
863                 error = true;
864                 return "";
865         }
866         // odd number of quotes. thats just wrong.
867         if ((number_of_quotes % 2) != 0)
868         {
869                 *errorstream << "Missing \" at " << filename << ":" << linenumber << endl;
870                 error = true;
871                 return "";
872         }
873         if (number_of_equals < (number_of_quotes/2))
874         {
875                 *errorstream << "Missing '=' at " << filename << ":" << linenumber << endl;
876         }
877         if (number_of_equals > (number_of_quotes/2))
878         {
879                 *errorstream << "Too many '=' at " << filename << ":" << linenumber << endl;
880         }
881
882         std::string parsedata = buffer;
883         // turn multispace into single space
884         while (parsedata.find("\xA0\xA0") != std::string::npos)
885         {
886                 parsedata.erase(parsedata.find("\xA0\xA0"),1);
887         }
888
889         // turn our hardspace back into softspace
890         for (unsigned int d = 0; d < parsedata.length(); d++)
891         {
892                 if (parsedata[d] == '\xA0')
893                         parsedata[d] = ' ';
894         }
895
896         // and we're done, the line is fine!
897         return parsedata;
898 }
899
900 int ServerConfig::fgets_safe(char* buffer, size_t maxsize, FILE* &file)
901 {
902         char c_read = 0;
903         size_t n = 0;
904         char* bufptr = buffer;
905         while ((!feof(file)) && (c_read != '\n') && (c_read != '\r') && (n < maxsize))
906         {
907                 c_read = fgetc(file);
908                 if ((c_read != '\n') && (c_read != '\r'))
909                 {
910                         *bufptr++ = c_read;
911                         n++;
912                 }
913         }
914         *bufptr = 0;
915         return bufptr - buffer;
916 }
917
918 bool ServerConfig::LoadConf(const char* filename, std::stringstream *target, std::stringstream* errorstream)
919 {
920         target->str("");
921         errorstream->str("");
922         long linenumber = 1;
923         // first, check that the file exists before we try to do anything with it
924         if (!FileExists(filename))
925         {
926                 *errorstream << "File " << filename << " not found." << endl;
927                 return false;
928         }
929         // Fix the chmod of the file to restrict it to the current user and group
930         chmod(filename,0600);
931         for (unsigned int t = 0; t < include_stack.size(); t++)
932         {
933                 if (std::string(filename) == include_stack[t])
934                 {
935                         *errorstream << "File " << filename << " is included recursively (looped inclusion)." << endl;
936                         return false;
937                 }
938         }
939         include_stack.push_back(filename);
940         // now open it
941         FILE* conf = fopen(filename,"r");
942         char buffer[MAXBUF];
943         if (conf)
944         {
945                 while (!feof(conf))
946                 {
947                         if (fgets_safe(buffer, MAXBUF, conf))
948                         {
949                                 if ((!feof(conf)) && (buffer) && (*buffer))
950                                 {
951                                         if ((buffer[0] != '#') && (buffer[0] != '\r')  && (buffer[0] != '\n'))
952                                         {
953                                                 if (!strncmp(buffer,"<include file=\"",15))
954                                                 {
955                                                         char* buf = buffer;
956                                                         char confpath[10240],newconf[10240];
957                                                         // include file directive
958                                                         buf += 15;      // advance to filename
959                                                         for (char* j = buf; *j; j++)
960                                                         {
961                                                                 if (*j == '\\')
962                                                                         *j = '/';
963                                                                 if (*j == '"')
964                                                                 {
965                                                                         *j = 0;
966                                                                         break;
967                                                                 }
968                                                         }
969                                                         log(DEBUG,"Opening included file '%s'",buf);
970                                                         if (*buf != '/')
971                                                         {
972                                                                 strlcpy(confpath,CONFIG_FILE,10240);
973                                                                 if (strstr(confpath,"/inspircd.conf"))
974                                                                 {
975                                                                         // leaves us with just the path
976                                                                         *(strstr(confpath,"/inspircd.conf")) = '\0';
977                                                                 }
978                                                                 snprintf(newconf,10240,"%s/%s",confpath,buf);
979                                                         }
980                                                         else strlcpy(newconf,buf,10240);
981                                                         std::stringstream merge(stringstream::in | stringstream::out);
982                                                         // recursively call LoadConf and get the new data, use the same errorstream
983                                                         if (LoadConf(newconf, &merge, errorstream))
984                                                         {
985                                                                 // append to the end of the file
986                                                                 std::string newstuff = merge.str();
987                                                                 *target << newstuff;
988                                                         }
989                                                         else
990                                                         {
991                                                                 // the error propogates up to its parent recursively
992                                                                 // causing the config reader to bail at the top level.
993                                                                 fclose(conf);
994                                                                 return false;
995                                                         }
996                                                 }
997                                                 else
998                                                 {
999                                                         bool error = false;
1000                                                         std::string data = this->ConfProcess(buffer,linenumber++,errorstream,error,filename);
1001                                                         if (error)
1002                                                         {
1003                                                                 return false;
1004                                                         }
1005                                                         *target << data;
1006                                                 }
1007                                         }
1008                                         else linenumber++;
1009                                 }
1010                         }
1011                 }
1012                 fclose(conf);
1013         }
1014         target->seekg(0);
1015         return true;
1016 }
1017
1018 /* Counts the number of tags of a certain type within the config file, e.g. to enumerate opers */
1019
1020 int ServerConfig::EnumConf(std::stringstream *config, const char* tag)
1021 {
1022         int ptr = 0;
1023         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
1024         int in_token, in_quotes, tptr, idx = 0;
1025
1026         std::string x = config->str();
1027         const char* buf = x.c_str();
1028         char* bptr = (char*)buf;
1029         
1030         ptr = 0;
1031         in_token = 0;
1032         in_quotes = 0;
1033         lastc = '\0';
1034         while (*bptr)
1035         {
1036                 lastc = c;
1037                 c = *bptr++;
1038                 if ((c == '#') && (lastc == '\n'))
1039                 {
1040                         while ((c != '\n') && (*bptr))
1041                         {
1042                                 lastc = c;
1043                                 c = *bptr++;
1044                         }
1045                 }
1046                 if ((c == '<') && (!in_quotes))
1047                 {
1048                         tptr = 0;
1049                         in_token = 1;
1050                         do {
1051                                 c = *bptr++;
1052                                 if (c != ' ')
1053                                 {
1054                                         c_tag[tptr++] = c;
1055                                         c_tag[tptr] = '\0';
1056                                 }
1057                         } while (c != ' ');
1058                 }
1059                 if (c == '"')
1060                 {
1061                         in_quotes = (!in_quotes);
1062                 }
1063                 if ((c == '>') && (!in_quotes))
1064                 {
1065                         in_token = 0;
1066                         if (!strcmp(c_tag,tag))
1067                         {
1068                                 /* correct tag, but wrong index */
1069                                 idx++;
1070                         }
1071                         c_tag[0] = '\0';
1072                         buffer[0] = '\0';
1073                         ptr = 0;
1074                         tptr = 0;
1075                 }
1076                 if (c != '>')
1077                 {
1078                         if ((in_token) && (c != '\n') && (c != '\r'))
1079                         {
1080                                 buffer[ptr++] = c;
1081                                 buffer[ptr] = '\0';
1082                         }
1083                 }
1084         }
1085         return idx;
1086 }
1087
1088 /* Counts the number of values within a certain tag */
1089
1090 int ServerConfig::EnumValues(std::stringstream *config, const char* tag, int index)
1091 {
1092         int ptr = 0;
1093         char buffer[MAXBUF], c_tag[MAXBUF], c, lastc;
1094         int in_token, in_quotes, tptr, idx = 0;
1095         bool correct_tag = false;
1096         int num_items = 0;
1097         const char* buf = config->str().c_str();
1098         char* bptr = (char*)buf;
1099
1100         ptr = 0;
1101         in_token = 0;
1102         in_quotes = 0;
1103         lastc = 0;
1104
1105         while (*bptr)
1106         {
1107                 lastc = c;
1108                 c = *bptr++;
1109                 if ((c == '#') && (lastc == '\n'))
1110                 {
1111                         while ((c != '\n') && (*bptr))
1112                         {
1113                                 lastc = c;
1114                                 c = *bptr++;
1115                         }
1116                 }
1117                 if ((c == '<') && (!in_quotes))
1118                 {
1119                         tptr = 0;
1120                         in_token = 1;
1121                         do {
1122                                 c = *bptr++;
1123                                 if (c != ' ')
1124                                 {
1125                                         c_tag[tptr++] = c;
1126                                         c_tag[tptr] = '\0';
1127                                         
1128                                         if ((!strcmp(c_tag,tag)) && (idx == index))
1129                                         {
1130                                                 correct_tag = true;
1131                                         }
1132                                 }
1133                         } while (c != ' ');
1134                 }
1135                 if (c == '"')
1136                 {
1137                         in_quotes = (!in_quotes);
1138                 }
1139                 
1140                 if ( (correct_tag) && (!in_quotes) && ( (c == ' ') || (c == '\n') || (c == '\r') ) )
1141                 {
1142                         num_items++;
1143                 }
1144                 if ((c == '>') && (!in_quotes))
1145                 {
1146                         in_token = 0;
1147                         if (correct_tag)
1148                                 correct_tag = false;
1149                         if (!strcmp(c_tag,tag))
1150                         {
1151                                 /* correct tag, but wrong index */
1152                                 idx++;
1153                         }
1154                         c_tag[0] = '\0';
1155                         buffer[0] = '\0';
1156                         ptr = 0;
1157                         tptr = 0;
1158                 }
1159                 if (c != '>')
1160                 {
1161                         if ((in_token) && (c != '\n') && (c != '\r'))
1162                         {
1163                                 buffer[ptr++] = c;
1164                                 buffer[ptr] = '\0';
1165                         }
1166                 }
1167         }
1168         return num_items+1;
1169 }
1170
1171
1172 int ServerConfig::ConfValueEnum(char* tag, std::stringstream* config)
1173 {
1174         return EnumConf(config,tag);
1175 }
1176
1177
1178 int ServerConfig::ReadConf(std::stringstream *config, const char* tag, const char* var, int index, char *result)
1179 {
1180         int ptr = 0;
1181         char buffer[65535], c_tag[MAXBUF], c, lastc;
1182         int in_token, in_quotes, tptr, idx = 0;
1183         char* key;
1184         std::string x = config->str();
1185         const char* buf = x.c_str();
1186         char* bptr = (char*)buf;
1187         
1188         ptr = 0;
1189         in_token = 0;
1190         in_quotes = 0;
1191         lastc = 0;
1192         c_tag[0] = 0;
1193         buffer[0] = 0;
1194
1195         while (*bptr)
1196         {
1197                 lastc = c;
1198                 c = *bptr++;
1199                 // FIX: Treat tabs as spaces
1200                 if (c == 9)
1201                         c = 32;
1202                 if ((c == '<') && (!in_quotes))
1203                 {
1204                         tptr = 0;
1205                         in_token = 1;
1206                         do {
1207                                 c = *bptr++;
1208                                 if (c != ' ')
1209                                 {
1210                                         c_tag[tptr++] = c;
1211                                         c_tag[tptr] = '\0';
1212                                 }
1213                         // FIX: Tab can follow a tagname as well as space.
1214                         } while ((c != ' ') && (c != 9));
1215                 }
1216                 if (c == '"')
1217                 {
1218                         in_quotes = (!in_quotes);
1219                 }
1220                 if ((c == '>') && (!in_quotes))
1221                 {
1222                         in_token = 0;
1223                         if (idx == index)
1224                         {
1225                                 if (!strcmp(c_tag,tag))
1226                                 {
1227                                         if ((buffer) && (c_tag) && (var))
1228                                         {
1229                                                 key = strstr(buffer,var);
1230                                                 if (!key)
1231                                                 {
1232                                                         /* value not found in tag */
1233                                                         *result = 0;
1234                                                         return 0;
1235                                                 }
1236                                                 else
1237                                                 {
1238                                                         key+=strlen(var);
1239                                                         while (*key !='"')
1240                                                         {
1241                                                                 if (!*key)
1242                                                                 {
1243                                                                         /* missing quote */
1244                                                                         *result = 0;
1245                                                                         return 0;
1246                                                                 }
1247                                                                 key++;
1248                                                         }
1249                                                         key++;
1250                                                         for (char* j = key; *j; j++)
1251                                                         {
1252                                                                 if (*j == '"')
1253                                                                 {
1254                                                                         *j = 0;
1255                                                                         break;
1256                                                                 }
1257                                                         }
1258                                                         strlcpy(result,key,MAXBUF);
1259                                                         return 1;
1260                                                 }
1261                                         }
1262                                 }
1263                         }
1264                         if (!strcmp(c_tag,tag))
1265                         {
1266                                 /* correct tag, but wrong index */
1267                                 idx++;
1268                         }
1269                         c_tag[0] = '\0';
1270                         buffer[0] = '\0';
1271                         ptr = 0;
1272                         tptr = 0;
1273                 }
1274                 if (c != '>')
1275                 {
1276                         if ((in_token) && (c != '\n') && (c != '\r'))
1277                         {
1278                                 buffer[ptr++] = c;
1279                                 buffer[ptr] = '\0';
1280                         }
1281                 }
1282         }
1283         *result = 0; // value or its tag not found at all
1284         return 0;
1285 }
1286
1287
1288
1289 int ServerConfig::ConfValue(char* tag, char* var, int index, char *result,std::stringstream *config)
1290 {
1291         ReadConf(config, tag, var, index, result);
1292         return 0;
1293 }
1294
1295 int ServerConfig::ConfValueInteger(char* tag, char* var, int index, std::stringstream *config)
1296 {
1297         char result[MAXBUF];
1298         ReadConf(config, tag, var, index, result);
1299         return atoi(result);
1300 }
1301
1302 // This will bind a socket to a port. It works for UDP/TCP
1303 int BindSocket (int sockfd, struct sockaddr_in client, struct sockaddr_in server, int port, char* addr)
1304 {
1305         memset((char *)&server,0,sizeof(server));
1306         struct in_addr addy;
1307         bool resolved = false;
1308         char resolved_addr[128];
1309
1310         if (*addr == '*')
1311                 *addr = 0;
1312
1313         if (*addr && !inet_aton(addr,&addy))
1314         {
1315                 /* If they gave a hostname, bind to the IP it resolves to */
1316                 if (CleanAndResolve(resolved_addr, addr, true))
1317                 {
1318                         inet_aton(resolved_addr,&addy);
1319                         log(DEFAULT,"Resolved binding '%s' -> '%s'",addr,resolved_addr);
1320                         server.sin_addr = addy;
1321                         resolved = true;
1322                 }
1323                 else
1324                 {
1325                         log(DEFAULT,"WARNING: Could not resolve '%s' to an IP for binding to on port %d",addr,port);
1326                         return(FALSE);
1327                 }
1328         }
1329         server.sin_family = AF_INET;
1330         if (!resolved)
1331         {
1332                 if (!*addr)
1333                 {
1334                         server.sin_addr.s_addr = htonl(INADDR_ANY);
1335                 }
1336                 else
1337                 {
1338                         server.sin_addr = addy;
1339                 }
1340         }
1341         server.sin_port = htons(port);
1342         if (bind(sockfd,(struct sockaddr*)&server,sizeof(server)) < 0)
1343         {
1344                 return(ERROR);
1345         }
1346         else
1347         {
1348                 log(DEBUG,"Bound port %s:%d",*addr ? addr : "*",port);
1349                 if (listen(sockfd, Config->MaxConn) == -1)
1350                 {
1351                         log(DEFAULT,"ERROR in listen(): %s",strerror(errno));
1352                         return(FALSE);
1353                 }
1354                 else
1355                 {
1356                         return(TRUE);
1357                 }
1358         }
1359 }
1360
1361
1362 // Open a TCP Socket
1363 int OpenTCPSocket (void)
1364 {
1365         int sockfd;
1366         int on = 1;
1367         struct linger linger = { 0 };
1368   
1369         if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
1370                 return (ERROR);
1371         else
1372         {
1373                 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
1374                 /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
1375                 linger.l_onoff = 1;
1376                 linger.l_linger = 1;
1377                 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (const char*)&linger,sizeof(linger));
1378                 return (sockfd);
1379         }
1380 }
1381
1382 int BindPorts()
1383 {
1384         char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
1385         sockaddr_in client,server;
1386         int clientportcount = 0;
1387         int BoundPortCount = 0;
1388
1389         for (int count = 0; count < Config->ConfValueEnum("bind",&Config->config_f); count++)
1390         {
1391                 Config->ConfValue("bind","port",count,configToken,&Config->config_f);
1392                 Config->ConfValue("bind","address",count,Addr,&Config->config_f);
1393                 Config->ConfValue("bind","type",count,Type,&Config->config_f);
1394
1395                 if ((!*Type) || (!strcmp(Type,"clients")))
1396                 {
1397                         // modules handle server bind types now
1398                         Config->ports[clientportcount] = atoi(configToken);
1399
1400                         // If the client put bind "*", this is an unrealism.
1401                         // We don't actually support this as documented, but
1402                         // i got fed up of people trying it, so now it converts
1403                         // it to an empty string meaning the same 'bind to all'.
1404                         if (*Addr == '*')
1405                                 *Addr = 0;
1406
1407                         strlcpy(Config->addrs[clientportcount],Addr,256);
1408                         clientportcount++;
1409                         log(DEBUG,"InspIRCd: startup: read binding %s:%s [%s] from config",Addr,configToken, Type);
1410                 }
1411         }
1412
1413         int PortCount = clientportcount;
1414
1415         for (int count = 0; count < PortCount; count++)
1416         {
1417                 if ((openSockfd[BoundPortCount] = OpenTCPSocket()) == ERROR)
1418                 {
1419                         log(DEBUG,"InspIRCd: startup: bad fd %lu binding port [%s:%d]",(unsigned long)openSockfd[BoundPortCount],Config->addrs[count],(unsigned long)Config->ports[count]);
1420                         return(ERROR);
1421                 }
1422
1423                 if (BindSocket(openSockfd[BoundPortCount],client,server,Config->ports[count],Config->addrs[count]) == ERROR)
1424                 {
1425                         log(DEFAULT,"InspIRCd: startup: failed to bind port [%s:%lu]: %s",Config->addrs[count],(unsigned long)Config->ports[count],strerror(errno));
1426                 }
1427                 else
1428                 {
1429                         /* well we at least bound to one socket so we'll continue */
1430                         BoundPortCount++;
1431                 }
1432         }
1433
1434         /* if we didn't bind to anything then abort */
1435         if (!BoundPortCount)
1436         {
1437                 log(DEFAULT,"InspIRCd: startup: no ports bound, bailing!");
1438                 printf("\nERROR: Was not able to bind any of %lu ports! Please check your configuration.\n\n", (unsigned long)PortCount);
1439                 return (ERROR);
1440         }
1441
1442         return BoundPortCount;
1443 }
1444