* ---------------------------------------------------
*/
-/* $Core */
-
#include "inspircd.h"
#include "xline.h"
#include "socket.h"
#endif
-// version is a simple class for holding a modules version number
+// Version is a simple class for holding a modules version number
template<>
-VersionBase<API_VERSION>::VersionBase(const std::string &modv, int flags, int, const std::string& rev)
+VersionBase<API_VERSION>::VersionBase(const std::string &modv, int flags, const std::string& rev)
: description(modv), version(rev), Flags(flags)
{
}
// These declarations define the behavours of the base class Module (which does nothing at all)
Module::Module() { }
-bool Module::cull()
+CullResult Module::cull()
{
- ServerInstance->GlobalCulls.AddItem(ModuleDLLManager);
- return true;
+ return classbase::cull();
}
Module::~Module() { }
ModResult Module::OnPreMode(User*, User*, Channel*, const std::vector<std::string>&) { return MOD_RES_PASSTHRU; }
void Module::On005Numeric(std::string&) { }
ModResult Module::OnKill(User*, User*, const std::string&) { return MOD_RES_PASSTHRU; }
-void Module::OnLoadModule(Module*, const std::string&) { }
-void Module::OnUnloadModule(Module*, const std::string&) { }
+void Module::OnLoadModule(Module*) { }
+void Module::OnUnloadModule(Module*) { }
void Module::OnBackgroundTimer(time_t) { }
ModResult Module::OnPreCommand(std::string&, std::vector<std::string>&, User *, bool, const std::string&) { return MOD_RES_PASSTHRU; }
void Module::OnPostCommand(const std::string&, const std::vector<std::string>&, User *, CmdResult, const std::string&) { }
}
this->ModCount++;
- FOREACH_MOD(I_OnLoadModule,OnLoadModule(newmod, filename_str));
+ FOREACH_MOD(I_OnLoadModule,OnLoadModule(newmod));
/* We give every module a chance to re-prioritize when we introduce a new one,
* not just the one thats loading, as the new module could affect the preference
return true;
}
-bool ModuleManager::Unload(const char* filename)
+bool ModuleManager::CanUnload(Module* mod)
{
- std::string filename_str(filename);
- std::map<std::string, Module*>::iterator modfind = Modules.find(filename);
+ std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile);
- if (modfind != Modules.end())
+ if (modfind == Modules.end() || modfind->second != mod)
{
- if (modfind->second->GetVersion().Flags & VF_STATIC)
- {
- LastModuleError = "Module " + filename_str + " not unloadable (marked static)";
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
- return false;
- }
- std::pair<int,std::string> intercount = GetInterfaceInstanceCount(modfind->second);
- if (intercount.first > 0)
- {
- LastModuleError = "Failed to unload module " + filename_str + ", being used by " + ConvToStr(intercount.first) + " other(s) via interface '" + intercount.second + "'";
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
- return false;
- }
+ LastModuleError = "Module " + mod->ModuleSourceFile + " is not loaded, cannot unload it!";
+ ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ return false;
+ }
+ if (mod->GetVersion().Flags & VF_STATIC)
+ {
+ LastModuleError = "Module " + mod->ModuleSourceFile + " not unloadable (marked static)";
+ ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ return false;
+ }
+ std::pair<int,std::string> intercount = GetInterfaceInstanceCount(mod);
+ if (intercount.first > 0)
+ {
+ LastModuleError = "Failed to unload module " + mod->ModuleSourceFile + ", being used by " + ConvToStr(intercount.first) + " other(s) via interface '" + intercount.second + "'";
+ ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ return false;
+ }
+ return true;
+}
- std::vector<ExtensionItem*> items = Extensible::BeginUnregister(modfind->second);
- /* Give the module a chance to tidy out all its metadata */
- for (chan_hash::iterator c = ServerInstance->chanlist->begin(); c != ServerInstance->chanlist->end(); c++)
- {
- modfind->second->OnCleanup(TYPE_CHANNEL,c->second);
- c->second->doUnhookExtensions(items);
- const UserMembList* users = c->second->GetUsers();
- for(UserMembCIter mi = users->begin(); mi != users->end(); mi++)
- mi->second->doUnhookExtensions(items);
- }
- for (user_hash::iterator u = ServerInstance->Users->clientlist->begin(); u != ServerInstance->Users->clientlist->end(); u++)
- {
- modfind->second->OnCleanup(TYPE_USER,u->second);
- u->second->doUnhookExtensions(items);
- }
+void ModuleManager::DoSafeUnload(Module* mod)
+{
+ std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile);
- /* Tidy up any dangling resolvers */
- ServerInstance->Res->CleanResolvers(modfind->second);
+ std::vector<ExtensionItem*> items;
+ ServerInstance->Extensions.BeginUnregister(modfind->second, items);
+ /* Give the module a chance to tidy out all its metadata */
+ for (chan_hash::iterator c = ServerInstance->chanlist->begin(); c != ServerInstance->chanlist->end(); c++)
+ {
+ mod->OnCleanup(TYPE_CHANNEL,c->second);
+ c->second->doUnhookExtensions(items);
+ const UserMembList* users = c->second->GetUsers();
+ for(UserMembCIter mi = users->begin(); mi != users->end(); mi++)
+ mi->second->doUnhookExtensions(items);
+ }
+ for (user_hash::iterator u = ServerInstance->Users->clientlist->begin(); u != ServerInstance->Users->clientlist->end(); u++)
+ {
+ mod->OnCleanup(TYPE_USER,u->second);
+ u->second->doUnhookExtensions(items);
+ }
- FOREACH_MOD(I_OnUnloadModule,OnUnloadModule(modfind->second, modfind->first));
+ /* Tidy up any dangling resolvers */
+ ServerInstance->Res->CleanResolvers(mod);
- this->DetachAll(modfind->second);
+ FOREACH_MOD(I_OnUnloadModule,OnUnloadModule(mod));
- ServerInstance->GlobalCulls.AddItem(modfind->second);
- Modules.erase(modfind);
+ DetachAll(mod);
- ServerInstance->Logs->Log("MODULE", DEFAULT,"Module %s unloaded",filename);
- this->ModCount--;
- ServerInstance->BuildISupport();
- return true;
- }
+ Modules.erase(modfind);
+ ServerInstance->GlobalCulls.AddItem(mod);
- LastModuleError = "Module " + filename_str + " is not loaded, cannot unload it!";
- ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
- return false;
+ ServerInstance->Logs->Log("MODULE", DEFAULT,"Module %s unloaded",mod->ModuleSourceFile.c_str());
+ this->ModCount--;
+ ServerInstance->BuildISupport();
+}
+
+namespace {
+ struct UnloadAction : public HandlerBase0<void>
+ {
+ Module* const mod;
+ UnloadAction(Module* m) : mod(m) {}
+ void Call()
+ {
+ DLLManager* dll = mod->ModuleDLLManager;
+ ServerInstance->Modules->DoSafeUnload(mod);
+ ServerInstance->GlobalCulls.Apply();
+ delete dll;
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
+ };
+
+ struct ReloadAction : public HandlerBase0<void>
+ {
+ Module* const mod;
+ HandlerBase1<void, bool>* const callback;
+ ReloadAction(Module* m, HandlerBase1<void, bool>* c)
+ : mod(m), callback(c) {}
+ void Call()
+ {
+ DLLManager* dll = mod->ModuleDLLManager;
+ std::string name = mod->ModuleSourceFile;
+ ServerInstance->Modules->DoSafeUnload(mod);
+ ServerInstance->GlobalCulls.Apply();
+ delete dll;
+ bool rv = ServerInstance->Modules->Load(name.c_str());
+ callback->Call(rv);
+ ServerInstance->GlobalCulls.AddItem(this);
+ }
+ };
+}
+
+bool ModuleManager::Unload(Module* mod)
+{
+ if (!CanUnload(mod))
+ return false;
+ ServerInstance->AtomicActions.AddAction(new UnloadAction(mod));
+ return true;
+}
+
+void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
+{
+ if (CanUnload(mod))
+ ServerInstance->AtomicActions.AddAction(new ReloadAction(mod, callback));
+ else
+ callback->Call(false);
}
/* We must load the modules AFTER initializing the socket engine, now */
void ModuleManager::LoadAll()
{
- char configToken[MAXBUF];
ModCount = 0;
printf("\nLoading core commands");
printf("\n");
}
- for(int count = 0; count < ServerInstance->Config->ConfValueEnum("module"); count++)
+ for(int count = 0;; count++)
{
- ServerInstance->Config->ConfValue("module", "name", count, configToken, MAXBUF);
- printf_c("[\033[1;32m*\033[0m] Loading module:\t\033[1;32m%s\033[0m\n",configToken);
+ ConfigTag* tag = ServerInstance->Config->ConfValue("module", count);
+ if (!tag)
+ break;
+ std::string name = tag->getString("name");
+ printf_c("[\033[1;32m*\033[0m] Loading module:\t\033[1;32m%s\033[0m\n",name.c_str());
- if (!this->Load(configToken))
+ if (!this->Load(name.c_str()))
{
ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
printf_c("\n[\033[1;31m*\033[0m] %s\n\n", this->LastError().c_str());
}
}
+void ModuleManager::UnloadAll()
+{
+ /* We do this more than once, so that any service providers get a
+ * chance to be unhooked by the modules using them, but then get
+ * a chance to be removed themsleves.
+ *
+ * Note: this deliberately does NOT delete the DLLManager objects
+ */
+ for (int tries = 0; tries < 4; tries++)
+ {
+ std::map<std::string, Module*>::iterator i = Modules.begin();
+ while (i != Modules.end())
+ {
+ std::map<std::string, Module*>::iterator me = i++;
+ if (CanUnload(me->second))
+ {
+ ServerInstance->GlobalCulls.AddItem(me->second);
+ Modules.erase(me);
+ }
+ }
+ ServerInstance->GlobalCulls.Apply();
+ }
+}
+
bool ModuleManager::PublishFeature(const std::string &FeatureName, Module* Mod)
{
if (Features.find(FeatureName) == Features.end())
std::string ConfigReader::ReadValue(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool allow_linefeeds)
{
/* Don't need to strlcpy() tag and name anymore, ReadConf() takes const char* */
- std::string result;
-
- if (!ServerInstance->Config->ConfValue(tag, name, default_value, index, result, allow_linefeeds))
+ std::string result = default_value;
+ if (!ServerInstance->Config->ConfValue(tag, index)->readString(name, result, allow_linefeeds))
{
this->error = CONF_VALUE_NOT_FOUND;
}
bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, const std::string &default_value, int index)
{
- return ServerInstance->Config->ConfValueBool(tag, name, default_value, index);
+ bool def = (default_value == "yes");
+ return ServerInstance->Config->ConfValue(tag, index)->getBool(name, def);
}
bool ConfigReader::ReadFlag(const std::string &tag, const std::string &name, int index)
int ConfigReader::ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool need_positive)
{
- int result;
-
- if(!ServerInstance->Config->ConfValueInteger(tag, name, default_value, index, result))
- {
- this->error = CONF_VALUE_NOT_FOUND;
- return 0;
- }
+ int v = atoi(default_value.c_str());
+ int result = ServerInstance->Config->ConfValue(tag, index)->getInt(name, v);
if ((need_positive) && (result < 0))
{
int ConfigReader::Enumerate(const std::string &tag)
{
- return ServerInstance->Config->ConfValueEnum(tag);
-}
-
-int ConfigReader::EnumerateValues(const std::string &tag, int index)
-{
- return ServerInstance->Config->ConfVarEnum(tag, index);
+ ServerInstance->Logs->Log("MODULE", DEBUG, "Module is using ConfigReader::Enumerate on %s; this is slow!",
+ tag.c_str());
+ int i=0;
+ while (ServerInstance->Config->ConfValue(tag, i)) i++;
+ return i;
}
FileReader::FileReader(const std::string &filename)