X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fconfigreader.cpp;h=df7721e826adfe0d4bc5ac327fc3c318d8b49c0e;hb=92eda175245c3d3da6b41af5c52d1540a23a0dc3;hp=be2b52ae13f41c51656da5501e612718c0b46f14;hpb=1dd466029961608a5008d8596d50ecc9adf898a4;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/configreader.cpp b/src/configreader.cpp index be2b52ae1..df7721e82 100644 --- a/src/configreader.cpp +++ b/src/configreader.cpp @@ -47,7 +47,7 @@ ServerConfig::ServerConfig(InspIRCd* Instance) : ServerInstance(Instance) dns_timeout = DieDelay = 5; MaxTargets = 20; NetBufferSize = 10240; - SoftLimit = MAXCLIENTS; + SoftLimit = Instance->SE->GetMaxFds(); MaxConn = SOMAXCONN; MaxWhoResults = 0; debugging = 0; @@ -63,32 +63,12 @@ void ServerConfig::ClearStack() include_stack.clear(); } -Module* ServerConfig::GetIOHook(int port) -{ - std::map::iterator x = IOHookModule.find(port); - return (x != IOHookModule.end() ? x->second : NULL); -} - Module* ServerConfig::GetIOHook(BufferedSocket* is) { std::map::iterator x = SocketIOHookModule.find(is); return (x != SocketIOHookModule.end() ? x->second : NULL); } -bool ServerConfig::AddIOHook(int port, Module* iomod) -{ - if (!GetIOHook(port)) - { - IOHookModule[port] = iomod; - return true; - } - else - { - throw ModuleException("Port already hooked by another module"); - return false; - } -} - bool ServerConfig::AddIOHook(Module* iomod, BufferedSocket* is) { if (!GetIOHook(is)) @@ -104,17 +84,6 @@ bool ServerConfig::AddIOHook(Module* iomod, BufferedSocket* is) } } -bool ServerConfig::DelIOHook(int port) -{ - std::map::iterator x = IOHookModule.find(port); - if (x != IOHookModule.end()) - { - IOHookModule.erase(x); - return true; - } - return false; -} - bool ServerConfig::DelIOHook(BufferedSocket* is) { std::map::iterator x = SocketIOHookModule.find(is); @@ -157,7 +126,7 @@ void ServerConfig::Update005() void ServerConfig::Send005(User* user) { for (std::vector::iterator line = ServerInstance->Config->isupport.begin(); line != ServerInstance->Config->isupport.end(); line++) - user->WriteServ("005 %s %s", user->nick, line->c_str()); + user->WriteNumeric(005, "%s %s", user->nick, line->c_str()); } bool ServerConfig::CheckOnce(const char* tag) @@ -292,10 +261,10 @@ bool ValidateMaxTargets(ServerConfig* conf, const char*, const char*, ValueItem bool ValidateSoftLimit(ServerConfig* conf, const char*, const char*, ValueItem &data) { - if ((data.GetInteger() < 1) || (data.GetInteger() > MAXCLIENTS)) + if ((data.GetInteger() < 1) || (data.GetInteger() > conf->GetInstance()->SE->GetMaxFds())) { - conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"WARNING: value is greater than %d or less than 0, set to %d.",MAXCLIENTS,MAXCLIENTS); - data.Set(MAXCLIENTS); + conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"WARNING: value is greater than %d or less than 0, set to %d.",conf->GetInstance()->SE->GetMaxFds(),conf->GetInstance()->SE->GetMaxFds()); + data.Set(conf->GetInstance()->SE->GetMaxFds()); } return true; } @@ -368,14 +337,9 @@ bool ValidateDnsServer(ServerConfig* conf, const char*, const char*, ValueItem & bool ValidateServerName(ServerConfig* conf, const char*, const char*, ValueItem &data) { + conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"Validating server name"); /* If we already have a servername, and they changed it, we should throw an exception. */ - if ((strcasecmp(conf->ServerName, data.GetString())) && (*conf->ServerName)) - { - throw CoreException("Configuration error: You cannot change your servername at runtime! Please restart your server for this change to be applied."); - /* We don't actually reach this return of course... */ - return false; - } - if (!strchr(data.GetString(),'.')) + if (!strchr(data.GetString(), '.')) { conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"WARNING: '%s' is not a fully-qualified domain name. Changed to '%s%c'",data.GetString(),data.GetString(),'.'); std::string moo = std::string(data.GetString()).append("."); @@ -478,6 +442,8 @@ bool ValidateInvite(ServerConfig* conf, const char*, const char*, ValueItem &dat bool ValidateSID(ServerConfig* conf, const char*, const char*, ValueItem &data) { + conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"Validating server id"); + const char *sid = data.GetString(); if (*sid && !conf->GetInstance()->IsSID(sid)) @@ -485,6 +451,8 @@ bool ValidateSID(ServerConfig* conf, const char*, const char*, ValueItem &data) throw CoreException(std::string(sid) + " is not a valid server ID. A server ID must be 3 characters long, with the first character a digit and the next two characters a digit or letter."); } + strlcpy(conf->sid, sid, 5); + return true; } @@ -520,13 +488,6 @@ bool InitConnect(ServerConfig* conf, const char*) { conf->GetInstance()->Logs->Log("CONFIG",DEFAULT,"Reading connect classes..."); - for (ClassVector::iterator i = conf->Classes.begin(); i != conf->Classes.end(); i++) - { - ConnectClass *c = *i; - - conf->GetInstance()->Logs->Log("CONFIG",DEBUG, "Address of class is %p", c); - } - for (ClassVector::iterator i = conf->Classes.begin(); i != conf->Classes.end() ; ) { ConnectClass* c = *i; @@ -780,7 +741,7 @@ void ServerConfig::ReportConfigError(const std::string &errormessage, bool bail, ServerInstance->SNO->WriteToSnoMask('A', "There were errors in the configuration file:"); while (start < errors.length()) { - ServerInstance->SNO->WriteToSnoMask('A', errors.substr(start, 360).c_str()); + ServerInstance->SNO->WriteToSnoMask('A', errors.substr(start, 360)); start += 360; } } @@ -788,9 +749,9 @@ void ServerConfig::ReportConfigError(const std::string &errormessage, bool bail, } } -void ServerConfig::Read(bool bail, User* user, int pass) +void ServerConfig::Read(bool bail, User* user) { - int rem = 0, add = 0; /* Number of modules added, number of modules removed */ + int rem = 0, add = 0; /* Number of modules added, number of modules removed */ static char debug[MAXBUF]; /* Temporary buffer for debugging value */ static char maxkeep[MAXBUF]; /* Temporary buffer for WhoWasMaxKeep value */ @@ -799,18 +760,20 @@ void ServerConfig::Read(bool bail, User* user, int pass) static char announceinvites[MAXBUF]; /* options:announceinvites setting */ errstr.clear(); + include_stack.clear(); + /* These tags MUST occur and must ONLY occur once in the config file */ static const char* Once[] = { "server", "admin", "files", "power", "options", NULL }; /* These tags can occur ONCE or not at all */ InitialConfig Values[] = { - {"options", "softlimit", MAXCLIENTS_S, new ValueContainerUInt (&this->SoftLimit), DT_INTEGER, ValidateSoftLimit}, + {"options", "softlimit", "0", new ValueContainerUInt (&this->SoftLimit), DT_INTEGER, ValidateSoftLimit}, {"options", "somaxconn", SOMAXCONN_S, new ValueContainerInt (&this->MaxConn), DT_INTEGER, ValidateMaxConn}, {"options", "moronbanner", "Youre banned!", new ValueContainerChar (this->MoronBanner), DT_CHARPTR, NoValidation}, - {"server", "name", "", new ValueContainerChar (this->ServerName), DT_HOSTNAME, ValidateServerName}, + {"server", "name", "", new ValueContainerChar (this->ServerName), DT_HOSTNAME|DT_BOOTONLY, ValidateServerName}, {"server", "description", "Configure Me", new ValueContainerChar (this->ServerDesc), DT_CHARPTR, NoValidation}, {"server", "network", "Network", new ValueContainerChar (this->Network), DT_NOSPACES, NoValidation}, - {"server", "id", "", new ValueContainerChar (this->sid), DT_CHARPTR, ValidateSID}, + {"server", "id", "", new ValueContainerChar (this->sid), DT_CHARPTR|DT_BOOTONLY, ValidateSID}, {"admin", "name", "", new ValueContainerChar (this->AdminName), DT_CHARPTR, NoValidation}, {"admin", "email", "Mis@configu.red", new ValueContainerChar (this->AdminEmail), DT_CHARPTR, NoValidation}, {"admin", "nick", "Misconfigured", new ValueContainerChar (this->AdminNick), DT_CHARPTR, NoValidation}, @@ -931,9 +894,9 @@ void ServerConfig::Read(bool bail, User* user, int pass) InitTypes, DoType, DoneClassesAndTypes}, {"class", - {"name", "commands", NULL}, - {"", "", NULL}, - {DT_NOSPACES, DT_CHARPTR}, + {"name", "commands", "usermodes", "chanmodes", NULL}, + {"", "", "", "", NULL}, + {DT_NOSPACES, DT_CHARPTR, DT_CHARPTR, DT_CHARPTR}, InitClasses, DoClass, DoneClassesAndTypes}, {NULL, @@ -948,10 +911,12 @@ void ServerConfig::Read(bool bail, User* user, int pass) /* Make a copy here so if it fails then we can carry on running with an unaffected config */ newconfig.clear(); - if (this->LoadConf(newconfig, ServerInstance->ConfigFileName, errstr, pass)) + if (this->DoInclude(newconfig, ServerInstance->ConfigFileName, errstr)) { /* If we succeeded, set the ircd config to the new one */ + ServerInstance->Threads->Mutex(true); this->config_data = newconfig; + ServerInstance->Threads->Mutex(false); } else { @@ -962,6 +927,12 @@ void ServerConfig::Read(bool bail, User* user, int pass) /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */ try { + /* Check we dont have more than one of singular tags, or any of them missing + */ + for (int Index = 0; Once[Index]; Index++) + if (!CheckOnce(Once[Index])) + return; + /* Read the values of all the tags which occur once or not at all, and call their callbacks. */ for (int Index = 0; Values[Index].tag; Index++) @@ -970,8 +941,14 @@ void ServerConfig::Read(bool bail, User* user, int pass) int dt = Values[Index].datatype; bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0); bool allow_wild = ((dt & DT_ALLOW_WILD) > 0); + bool bootonly = ((dt & DT_BOOTONLY) > 0); dt &= ~DT_ALLOW_NEWLINE; dt &= ~DT_ALLOW_WILD; + dt &= ~DT_BOOTONLY; + + /* Silently ignore boot only values */ + if (bootonly && !bail) + continue; ConfValue(this->config_data, Values[Index].tag, Values[Index].value, Values[Index].default_value, 0, item, MAXBUF, allow_newlines); ValueItem vi(item); @@ -979,7 +956,8 @@ void ServerConfig::Read(bool bail, User* user, int pass) if (!Values[Index].validation_function(this, Values[Index].tag, Values[Index].value, vi)) throw CoreException("One or more values in your configuration file failed to validate. Please see your ircd.log for more information."); - switch (Values[Index].datatype) + ServerInstance->Threads->Mutex(true); + switch (dt) { case DT_NOSPACES: { @@ -993,6 +971,7 @@ void ServerConfig::Read(bool bail, User* user, int pass) ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val; this->ValidateHostname(vi.GetString(), Values[Index].tag, Values[Index].value); vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1); + ServerInstance->Logs->Log("CONFIG",DEFAULT,"Got %s", vi.GetString()); } break; case DT_IPADDRESS: @@ -1006,7 +985,10 @@ void ServerConfig::Read(bool bail, User* user, int pass) { ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val; if (*(vi.GetString()) && !ServerInstance->IsChannel(vi.GetString())) + { + ServerInstance->Threads->Mutex(false); throw CoreException("The value of <"+std::string(Values[Index].tag)+":"+Values[Index].value+"> is not a valid channel name"); + } vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1); } break; @@ -1037,6 +1019,7 @@ void ServerConfig::Read(bool bail, User* user, int pass) } /* We're done with this now */ delete Values[Index].val; + ServerInstance->Threads->Mutex(false); } /* Read the multiple-tag items (class tags, connect tags, etc) @@ -1045,7 +1028,9 @@ void ServerConfig::Read(bool bail, User* user, int pass) */ for (int Index = 0; MultiValues[Index].tag; Index++) { + ServerInstance->Threads->Mutex(true); MultiValues[Index].init_function(this, MultiValues[Index].tag); + ServerInstance->Threads->Mutex(false); int number_of_tags = ConfValueEnum(this->config_data, MultiValues[Index].tag); @@ -1060,81 +1045,93 @@ void ServerConfig::Read(bool bail, User* user, int pass) dt &= ~DT_ALLOW_NEWLINE; dt &= ~DT_ALLOW_WILD; - switch (dt) + ServerInstance->Threads->Mutex(true); + /* We catch and rethrow any exception here just so we can free our mutex + */ + try { - case DT_NOSPACES: - { - char item[MAXBUF]; - if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines)) - vl.push_back(ValueItem(item)); - else - vl.push_back(ValueItem("")); - this->ValidateNoSpaces(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]); - } - break; - case DT_HOSTNAME: - { - char item[MAXBUF]; - if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines)) - vl.push_back(ValueItem(item)); - else - vl.push_back(ValueItem("")); - this->ValidateHostname(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]); - } - break; - case DT_IPADDRESS: - { - char item[MAXBUF]; - if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines)) - vl.push_back(ValueItem(item)); - else - vl.push_back(ValueItem("")); - this->ValidateIP(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum], allow_wild); - } - break; - case DT_CHANNEL: - { - char item[MAXBUF]; - if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines)) - vl.push_back(ValueItem(item)); - else - vl.push_back(ValueItem("")); - if (!ServerInstance->IsChannel(vl[vl.size()-1].GetString())) - throw CoreException("The value of <"+std::string(MultiValues[Index].tag)+":"+MultiValues[Index].items[valuenum]+"> number "+ConvToStr(tagnum + 1)+" is not a valid channel name"); - } - break; - case DT_CHARPTR: - { - char item[MAXBUF]; - if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines)) - vl.push_back(ValueItem(item)); - else - vl.push_back(ValueItem("")); - } - break; - case DT_INTEGER: + switch (dt) { - int item = 0; - if (ConfValueInteger(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item)) + case DT_NOSPACES: + { + char item[MAXBUF]; + if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines)) + vl.push_back(ValueItem(item)); + else + vl.push_back(ValueItem("")); + this->ValidateNoSpaces(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]); + } + break; + case DT_HOSTNAME: + { + char item[MAXBUF]; + if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines)) + vl.push_back(ValueItem(item)); + else + vl.push_back(ValueItem("")); + this->ValidateHostname(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]); + } + break; + case DT_IPADDRESS: + { + char item[MAXBUF]; + if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines)) + vl.push_back(ValueItem(item)); + else + vl.push_back(ValueItem("")); + this->ValidateIP(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum], allow_wild); + } + break; + case DT_CHANNEL: + { + char item[MAXBUF]; + if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines)) + vl.push_back(ValueItem(item)); + else + vl.push_back(ValueItem("")); + if (!ServerInstance->IsChannel(vl[vl.size()-1].GetString())) + throw CoreException("The value of <"+std::string(MultiValues[Index].tag)+":"+MultiValues[Index].items[valuenum]+"> number "+ConvToStr(tagnum + 1)+" is not a valid channel name"); + } + break; + case DT_CHARPTR: + { + char item[MAXBUF]; + if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines)) + vl.push_back(ValueItem(item)); + else + vl.push_back(ValueItem("")); + } + break; + case DT_INTEGER: + { + int item = 0; + if (ConfValueInteger(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item)) + vl.push_back(ValueItem(item)); + else + vl.push_back(ValueItem(0)); + } + break; + case DT_BOOLEAN: + { + bool item = ConfValueBool(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum); vl.push_back(ValueItem(item)); - else - vl.push_back(ValueItem(0)); + } + break; + default: + /* Someone was smoking craq if we got here, and we're all gonna die. */ + break; } - break; - case DT_BOOLEAN: - { - bool item = ConfValueBool(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum); - vl.push_back(ValueItem(item)); - } - break; - default: - /* Someone was smoking craq if we got here, and we're all gonna die. */ - break; } + catch (CoreException &e) + { + ServerInstance->Threads->Mutex(false); + throw e; + } + ServerInstance->Threads->Mutex(false); } - MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, vl, MultiValues[Index].datatype); + MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, vl, MultiValues[Index].datatype); } - MultiValues[Index].finish_function(this, MultiValues[Index].tag); + MultiValues[Index].finish_function(this, MultiValues[Index].tag); } } @@ -1145,51 +1142,14 @@ void ServerConfig::Read(bool bail, User* user, int pass) return; } - /** XXX END PASS **/ - ServerInstance->Logs->Log("CONFIG",DEBUG,"End config pass %d", pass); - - if (pass == 0) - { - /* FIRST PASS: Set up commands, load modules. - * We cannot gaurantee that all config is correct - * at this point - */ - - if (pass == 0) - { - ServerInstance->Logs->Log("CONFIG",DEBUG, "Downloading configuration"); - - TotalDownloaded = 0; - FileErrors = 0; - } - - if (!ServerInstance->Res) - ServerInstance->Res = new DNS(ServerInstance); - /** Note: This is safe, the method checks for user == NULL */ - ServerInstance->Parser->SetupCommandTable(user); - ServerInstance->Modules->LoadAll(); - } - else - { - /* SECOND PASS: Call modules to read configs, finalize - * stuff. Check that we have at least the required number - * of whichever items. This is no longer done first. - */ - /* Close all logs at this point and reopen logs. */ - ServerInstance->Logs->CloseLogs(); - ServerInstance->Logs->OpenFileLogs(); - ConfigReader* n = new ConfigReader(ServerInstance); - FOREACH_MOD(I_OnReadConfig,OnReadConfig(this, n)); - - for (int Index = 0; Once[Index]; Index++) - if (!CheckOnce(Once[Index])) - return; - } - // write once here, to try it out and make sure its ok ServerInstance->WritePID(this->PID); - ServerInstance->Logs->Log("CONFIG",DEFAULT,"Done reading configuration file."); + /* Switch over logfiles */ + ServerInstance->Logs->CloseLogs(); + ServerInstance->Logs->OpenFileLogs(); + + ServerInstance->Logs->Log("CONFIG", DEFAULT, "Done reading configuration file."); /* If we're rehashing, let's load any new modules, and unload old ones */ @@ -1201,6 +1161,7 @@ void ServerConfig::Read(bool bail, User* user, int pass) if (pl.size() && user) { + ServerInstance->Threads->Mutex(true); user->WriteServ("NOTICE %s :*** Not all your client ports could be bound.", user->nick); user->WriteServ("NOTICE %s :*** The following port(s) failed to bind:", user->nick); int j = 1; @@ -1208,143 +1169,74 @@ void ServerConfig::Read(bool bail, User* user, int pass) { user->WriteServ("NOTICE %s :*** %d. IP: %s Port: %lu", user->nick, j, i->first.empty() ? "" : i->first.c_str(), (unsigned long)i->second); } + ServerInstance->Threads->Mutex(false); } - } - if (!removed_modules.empty()) - { - for (std::vector::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++) + ServerInstance->Threads->Mutex(true); + if (!removed_modules.empty()) { - if (ServerInstance->Modules->Unload(removing->c_str())) + for (std::vector::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++) { - ServerInstance->SNO->WriteToSnoMask('A', "REHASH UNLOADED MODULE: %s",removing->c_str()); - if (user) - user->WriteServ("973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str()); - rem++; - } - else - { - if (user) - user->WriteServ("972 %s %s :%s",user->nick, removing->c_str(), ServerInstance->Modules->LastError().c_str()); + if (ServerInstance->Modules->Unload(removing->c_str())) + { + ServerInstance->SNO->WriteToSnoMask('A', "*** REHASH UNLOADED MODULE: %s",removing->c_str()); + + if (user) + user->WriteNumeric(973, "%s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str()); + + rem++; + } + else + { + if (user) + user->WriteNumeric(972, "%s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ServerInstance->Modules->LastError().c_str()); + } } } - } - if (!added_modules.empty()) - { - for (std::vector::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++) + if (!added_modules.empty()) { - /* Skip over modules that are aleready loaded for some reason */ - if (ServerInstance->Modules->Find(*adding)) - continue; - - if (bail) - printf_c("[\033[1;32m*\033[0m] Loading module:\t\033[1;32m%s\033[0m\n", adding->c_str()); - - if (ServerInstance->Modules->Load(adding->c_str())) + for (std::vector::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++) { - ServerInstance->SNO->WriteToSnoMask('A', "REHASH LOADED MODULE: %s",adding->c_str()); - if (user) - user->WriteServ("975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str()); + if (ServerInstance->Modules->Load(adding->c_str())) + { + ServerInstance->SNO->WriteToSnoMask('A', "*** REHASH LOADED MODULE: %s",adding->c_str()); - add++; - } - else - { - if (user) - user->WriteServ("974 %s %s :%s",user->nick, adding->c_str(), ServerInstance->Modules->LastError().c_str()); + if (user) + user->WriteNumeric(975, "%s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str()); - if (bail) + add++; + } + else { - printf_c("\n[\033[1;31m*\033[0m] %s\n\n", ServerInstance->Modules->LastError().c_str()); - ServerInstance->Exit(EXIT_STATUS_MODULE); + if (user) + user->WriteNumeric(974, "%s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ServerInstance->Modules->LastError().c_str()); } } } + + ServerInstance->Logs->Log("CONFIG", 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()); + + ServerInstance->Threads->Mutex(false); + } - ServerInstance->Logs->Log("CONFIG",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()); + /** Note: This is safe, the method checks for user == NULL */ + ServerInstance->Threads->Mutex(true); + ServerInstance->Parser->SetupCommandTable(user); + ServerInstance->Threads->Mutex(false); if (user) user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick); else - ServerInstance->SNO->WriteToSnoMask('A', "Successfully rehashed server."); -} - -/* XXX: This can and will block! */ -int ServerConfig::DoDownloads() -{ - ServerInstance->Logs->Log("CONFIG",DEBUG,"In DoDownloads()"); - - int new_downloads = 0; - - /* Reads all local files into the IncludedFiles map, then initiates sockets for the remote ones */ - for (std::map::iterator x = IncludedFiles.begin(); x != IncludedFiles.end(); ++x) - { - if (CompletedFiles.find(x->first) != CompletedFiles.end()) - continue; - - new_downloads++; - - ServerInstance->Logs->Log("CONFIG",DEBUG,"StartDownloads File: %s", x->first.c_str()); - - std::string file = x->first; - if ((file[0] == '/') || (file.substr(0, 7) == "file://")) - { - /* For file:// schema files, we use std::ifstream which is a derivative of std::istream. - * For all other file schemas, we use a std::stringstream. - */ - - /* Add our own ifstream */ - std::ifstream* conf = new std::ifstream(file.c_str()); - if (!conf->fail()) - { - ServerInstance->Logs->Log("CONFIG",DEBUG,"file:// schema file %s loaded OK", file.c_str()); - delete x->second; - x->second = conf; - } - else - { - delete x->second; - x->second = NULL; - FileErrors++; - } - TotalDownloaded++; - } - else - { - /* Modules handle these */ - ServerInstance->Logs->Log("CONFIG",DEBUG,"Module-handled schema for %s", x->first.c_str()); + ServerInstance->SNO->WriteToSnoMask('A', "*** Successfully rehashed server."); - /* For now, error it */ - int MOD_RESULT = 0; - FOREACH_RESULT(I_OnDownloadFile, OnDownloadFile(file, x->second)); - if (MOD_RESULT == 0) - { - /* No module claimed this file */ - TotalDownloaded++; - FileErrors++; - delete x->second; - x->second = NULL; - } - else - { - /* Search new file here for more includes to parse */ - ServerInstance->Logs->Log("CONFIG",DEBUG,"Searching for further includes in %s", x->first.c_str()); - } - } - CompletedFiles[x->first] = true; - } - - ServerInstance->Logs->Log("CONFIG",DEBUG,"Returning %d from DoDownloads()", new_downloads); - - return new_downloads; } -bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream, int pass, std::istream *scan_for_includes_only) + +bool ServerConfig::LoadConf(ConfigDataHash &target, FILE* &conf, const char* filename, std::ostringstream &errorstream) { std::string line; - std::istream* conf = NULL; char ch; long linenumber; bool in_tag; @@ -1357,71 +1249,31 @@ bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::o in_quote = false; in_comment = false; - if (scan_for_includes_only) - { - ServerInstance->Logs->Log("CONFIG",DEBUG,"scan_for_includes_only set"); - conf = scan_for_includes_only; - } + ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading %s", filename); - if (std::string(filename) == CONFIG_FILE) + /* Check if the file open failed first */ + if (!conf) { - if (!scan_for_includes_only) - { - conf = new std::ifstream(filename); - if (conf->fail()) - { - errorstream << "File " << filename << " could not be opened." << std::endl; - return false; - } - } + errorstream << "LoadConf: Couldn't open config file: " << filename << std::endl; + return false; } - else + + for (unsigned int t = 0; t < include_stack.size(); t++) { - std::map::iterator x = IncludedFiles.find(filename); - if (x == IncludedFiles.end()) - { - if (pass == 0) - { - ServerInstance->Logs->Log("CONFIG",DEBUG,"Push include file %s onto map", filename); - /* First pass, we insert the file into a map, and just return true */ - IncludedFiles.insert(std::make_pair(filename,new std::stringstream)); - return true; - } - else - { - /* Second pass, look for the file in the map */ - ServerInstance->Logs->Log("CONFIG",DEBUG,"We are in the second pass, and %s is not in the map!", filename); - errorstream << "File " << filename << " could not be opened." << std::endl; - return false; - } - } - else + if (std::string(filename) == include_stack[t]) { - if (!scan_for_includes_only) - { - if (x->second) - { - ServerInstance->Logs->Log("CONFIG",DEBUG,"Retrieve conf"); - conf = IncludedFiles.find(filename)->second; - } - else - { - errorstream << "File " << filename << " could not be opened." << std::endl; - return false; - } - } + errorstream << "File " << filename << " is included recursively (looped inclusion)." << std::endl; + return false; } } - if (!conf) - return false; - - ServerInstance->Logs->Log("CONFIG",DEBUG,"Start to read conf %s %08lx", filename, conf); + /* It's not already included, add it to the list of files we've loaded */ + include_stack.push_back(filename); /* Start reading characters... */ - while (conf->get(ch)) + while (!feof(conf)) { - + ch = fgetc(conf); /* * Fix for moronic windows issue spotted by Adremelech. * Some windows editors save text files as utf-16, which is @@ -1433,8 +1285,6 @@ bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::o if ((character_count++ < 2) && (ch == '\xFF' || ch == '\xFE')) { errorstream << "File " << filename << " cannot be read, as it is encoded in braindead UTF-16. Save your file as plain ASCII!" << std::endl; - if (!scan_for_includes_only) - delete conf; return false; } @@ -1480,8 +1330,9 @@ bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::o { line += ch; char real_character; - if (conf->get(real_character)) + if (!feof(conf)) { + real_character = fgetc(conf); if (real_character == 'n') real_character = '\n'; line += real_character; @@ -1490,8 +1341,6 @@ bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::o else { errorstream << "End of file after a \\, what did you want to escape?: " << filename << ":" << linenumber << std::endl; - if (!scan_for_includes_only) - delete conf; return false; } } @@ -1506,8 +1355,6 @@ bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::o if (!in_quote) { errorstream << "Got another opening < when the first one wasn't closed: " << filename << ":" << linenumber << std::endl; - if (!scan_for_includes_only) - delete conf; return false; } } @@ -1516,8 +1363,6 @@ bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::o if (in_quote) { errorstream << "We're in a quote but outside a tag, interesting. " << filename << ":" << linenumber << std::endl; - if (!scan_for_includes_only) - delete conf; return false; } else @@ -1556,7 +1401,6 @@ bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::o } else if (ch == '>') { - if (!in_quote) { if (in_tag) { @@ -1568,20 +1412,14 @@ bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::o * LoadConf() and load the included config into the same ConfigDataHash */ - if (!this->ParseLine(target, line, linenumber, errorstream, pass, scan_for_includes_only)) - { - if (!scan_for_includes_only) - delete conf; + if (!this->ParseLine(target, line, linenumber, errorstream)) return false; - } line.clear(); } else { errorstream << "Got a closing > when we weren't inside a tag: " << filename << ":" << linenumber << std::endl; - if (!scan_for_includes_only) - delete conf; return false; } } @@ -1595,17 +1433,16 @@ bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::o is a newline missing from the end of the file: " << filename << ":" << linenumber << std::endl; } - if (!scan_for_includes_only) - delete conf; return true; } -bool ServerConfig::LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream, int pass, std::istream* scan_for_includs_only) + +bool ServerConfig::LoadConf(ConfigDataHash &target, FILE* &conf, const std::string &filename, std::ostringstream &errorstream) { - return this->LoadConf(target, filename.c_str(), errorstream, pass, scan_for_includs_only); + return this->LoadConf(target, conf, filename.c_str(), errorstream); } -bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long &linenumber, std::ostringstream &errorstream, int pass, std::istream* scan_for_includes_only) +bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long &linenumber, std::ostringstream &errorstream) { std::string tagname; std::string current_key; @@ -1694,9 +1531,8 @@ bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long &li } else { - /* Leaving quotes, we have the value */ - if (!scan_for_includes_only) - results.push_back(KeyVal(current_key, current_value)); + /* Leaving the quotes, we have the current value */ + results.push_back(KeyVal(current_key, current_value)); // std::cout << "<" << tagname << ":" << current_key << "> " << current_value << std::endl; @@ -1705,7 +1541,13 @@ bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long &li if ((tagname == "include") && (current_key == "file")) { - if (!this->DoInclude(target, current_value, errorstream, pass, scan_for_includes_only)) + if (!this->DoInclude(target, current_value, errorstream)) + return false; + } + else if ((tagname == "include") && (current_key == "executable")) + { + /* Pipe an executable and use its stdout as config data */ + if (!this->DoPipe(target, current_value, errorstream)) return false; } @@ -1725,13 +1567,28 @@ bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long &li } /* Finished parsing the tag, add it to the config hash */ - if (!scan_for_includes_only) - target.insert(std::pair (tagname, results)); + target.insert(std::pair (tagname, results)); return true; } -bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream, int pass, std::istream* scan_for_includes_only) +bool ServerConfig::DoPipe(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream) +{ + FILE* conf = popen(file.c_str(), "r"); + bool ret = false; + + if (conf) + { + ret = LoadConf(target, conf, file.c_str(), errorstream); + pclose(conf); + } + else + errorstream << "Couldn't execute: " << file << std::endl; + + return ret; +} + +bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream) { std::string confpath; std::string newfile; @@ -1757,7 +1614,18 @@ bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, st } } - return LoadConf(target, newfile, errorstream, pass, scan_for_includes_only); + FILE* conf = fopen(newfile.c_str(), "r"); + bool ret = false; + + if (conf) + { + ret = LoadConf(target, conf, newfile, errorstream); + fclose(conf); + } + else + errorstream << "Couldn't open config file: " << file << std::endl; + + return ret; } bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length, bool allow_linefeeds) @@ -2206,8 +2074,12 @@ bool InitClasses(ServerConfig* conf, const char*) { for (operclass_t::iterator n = conf->operclass.begin(); n != conf->operclass.end(); n++) { - if (n->second) - delete[] n->second; + if (n->second.commandlist) + delete[] n->second.commandlist; + if (n->second.cmodelist) + delete[] n->second.cmodelist; + if (n->second.umodelist) + delete[] n->second.umodelist; } } @@ -2230,12 +2102,31 @@ bool DoType(ServerConfig* conf, const char*, char**, ValueList &values, int*) /* * XXX should this be in a class? -- w00t */ -bool DoClass(ServerConfig* conf, const char*, char**, ValueList &values, int*) +bool DoClass(ServerConfig* conf, const char* tag, char**, ValueList &values, int*) { const char* ClassName = values[0].GetString(); const char* CommandList = values[1].GetString(); + const char* UModeList = values[2].GetString(); + const char* CModeList = values[3].GetString(); - conf->operclass[ClassName] = strnewdup(CommandList); + for (const char* c = UModeList; *c; ++c) + { + if ((*c < 'A' || *c > 'z') && *c != '*') + { + throw CoreException("Character " + std::string(1, *c) + " is not a valid mode in "); + } + } + for (const char* c = CModeList; *c; ++c) + { + if ((*c < 'A' || *c > 'z') && *c != '*') + { + throw CoreException("Character " + std::string(1, *c) + " is not a valid mode in "); + } + } + + conf->operclass[ClassName].commandlist = strnewdup(CommandList); + conf->operclass[ClassName].umodelist = strnewdup(UModeList); + conf->operclass[ClassName].cmodelist = strnewdup(CModeList); return true; } @@ -2323,12 +2214,10 @@ bool DoneELine(ServerConfig* conf, const char* tag) void ConfigReaderThread::Run() { - do - { - ServerInstance->Config->Read(true, NULL, 0); - } - while (ServerInstance->Config->DoDownloads() > 0); - - ServerInstance->Config->Read(true, NULL, 1); + /* TODO: TheUser may be invalid by the time we get here! Check its validity, or pass a UID would be better */ + ServerInstance->Config->Read(do_bail, TheUser); + ServerInstance->Threads->Mutex(true); + this->SetExitFlag(); + ServerInstance->Threads->Mutex(false); }