/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2020 Matt Schatz <genius3000@g3k.solutions>
+ * Copyright (C) 2019 nia <nia@netbsd.org>
+ * Copyright (C) 2019 iwalkalone <iwalkalone69@gmail.com>
+ * Copyright (C) 2013, 2017-2020 Sadie Powell <sadie@witchery.services>
+ * Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ * Copyright (C) 2012-2016, 2018 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2012 Robby <robby@chatbelgie.be>
* Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2003-2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org>
* Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- * Copyright (C) 2006-2007 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2006-2007 Oliver Lupton <oliverlupton@gmail.com>
- * Copyright (C) 2007 Pippijn van Steenhoven <pip88nl@gmail.com>
- * Copyright (C) 2003 randomdan <???@???>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2007 Oliver Lupton <om@inspircd.org>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2005-2010 Craig Edwards <brain@inspircd.org>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
*/
-#include <iostream>
#include "inspircd.h"
#include "exitcodes.h"
-
-#ifndef _WIN32
- #include <dirent.h>
-#endif
+#include <iostream>
static insp::intrusive_list<dynamic_reference_base>* dynrefs = NULL;
// These declarations define the behavours of the base class Module (which does nothing at all)
-Module::Module() { }
+Module::Module()
+ : ModuleDLLManager(NULL)
+ , dying(false)
+{
+}
+
CullResult Module::cull()
{
+ if (ModuleDLLManager)
+ ServerInstance->GlobalCulls.AddItem(ModuleDLLManager);
return classbase::cull();
}
+
Module::~Module()
{
}
void Module::ReadConfig(ConfigStatus& status) { }
ModResult Module::OnSendSnotice(char &snomask, std::string &type, const std::string &message) { DetachEvent(I_OnSendSnotice); return MOD_RES_PASSTHRU; }
void Module::OnUserConnect(LocalUser*) { DetachEvent(I_OnUserConnect); }
+ModResult Module::OnUserPreQuit(LocalUser*, std::string&, std::string&) { DetachEvent(I_OnUserPreQuit); return MOD_RES_PASSTHRU; }
void Module::OnUserQuit(User*, const std::string&, const std::string&) { DetachEvent(I_OnUserQuit); }
void Module::OnUserDisconnect(LocalUser*) { DetachEvent(I_OnUserDisconnect); }
void Module::OnUserJoin(Membership*, bool, bool, CUList&) { DetachEvent(I_OnUserJoin); }
void Module::OnUnloadModule(Module*) { DetachEvent(I_OnUnloadModule); }
void Module::OnBackgroundTimer(time_t) { DetachEvent(I_OnBackgroundTimer); }
ModResult Module::OnPreCommand(std::string&, CommandBase::Params&, LocalUser*, bool) { DetachEvent(I_OnPreCommand); return MOD_RES_PASSTHRU; }
-void Module::OnPostCommand(Command*, const CommandBase::Params&, LocalUser*, CmdResult) { DetachEvent(I_OnPostCommand); }
+void Module::OnPostCommand(Command*, const CommandBase::Params&, LocalUser*, CmdResult, bool) { DetachEvent(I_OnPostCommand); }
+void Module::OnCommandBlocked(const std::string&, const CommandBase::Params&, LocalUser*) { DetachEvent(I_OnCommandBlocked); }
void Module::OnUserInit(LocalUser*) { DetachEvent(I_OnUserInit); }
+void Module::OnUserPostInit(LocalUser*) { DetachEvent(I_OnUserPostInit); }
ModResult Module::OnCheckReady(LocalUser*) { DetachEvent(I_OnCheckReady); return MOD_RES_PASSTHRU; }
ModResult Module::OnUserRegister(LocalUser*) { DetachEvent(I_OnUserRegister); return MOD_RES_PASSTHRU; }
ModResult Module::OnUserPreKick(User*, Membership*, const std::string&) { DetachEvent(I_OnUserPreKick); return MOD_RES_PASSTHRU; }
void Module::OnPostTopicChange(User*, Channel*, const std::string&) { DetachEvent(I_OnPostTopicChange); }
void Module::OnDecodeMetaData(Extensible*, const std::string&, const std::string&) { DetachEvent(I_OnDecodeMetaData); }
void Module::OnChangeHost(User*, const std::string&) { DetachEvent(I_OnChangeHost); }
+void Module::OnChangeRealHost(User*, const std::string&) { DetachEvent(I_OnChangeRealHost); }
void Module::OnChangeRealName(User*, const std::string&) { DetachEvent(I_OnChangeRealName); }
void Module::OnChangeIdent(User*, const std::string&) { DetachEvent(I_OnChangeIdent); }
void Module::OnAddLine(User*, XLine*) { DetachEvent(I_OnAddLine); }
void Module::OnDelLine(User*, XLine*) { DetachEvent(I_OnDelLine); }
void Module::OnExpireLine(XLine*) { DetachEvent(I_OnExpireLine); }
-void Module::OnCleanup(ExtensionItem::ExtensibleType, Extensible*) { }
+void Module::OnCleanup(ExtensionItem::ExtensibleType, Extensible*) { }
ModResult Module::OnChannelPreDelete(Channel*) { DetachEvent(I_OnChannelPreDelete); return MOD_RES_PASSTHRU; }
void Module::OnChannelDelete(Channel*) { DetachEvent(I_OnChannelDelete); }
void Module::OnBuildNeighborList(User*, IncludeChanList&, std::map<User*,bool>&) { DetachEvent(I_OnBuildNeighborList); }
void Module::OnGarbageCollect() { DetachEvent(I_OnGarbageCollect); }
ModResult Module::OnSetConnectClass(LocalUser* user, ConnectClass* myclass) { DetachEvent(I_OnSetConnectClass); return MOD_RES_PASSTHRU; }
-void Module::OnUserMessage(User*, const MessageTarget&, const MessageDetails&) { DetachEvent(I_OnUserMessage); }
-ModResult Module::OnNamesListItem(User*, Membership*, std::string&, std::string&) { DetachEvent(I_OnNamesListItem); return MOD_RES_PASSTHRU; }
+void Module::OnUserMessage(User*, const MessageTarget&, const MessageDetails&) { DetachEvent(I_OnUserMessage); }
ModResult Module::OnNumeric(User*, const Numeric::Numeric&) { DetachEvent(I_OnNumeric); return MOD_RES_PASSTHRU; }
ModResult Module::OnAcceptConnection(int, ListenSocket*, irc::sockets::sockaddrs*, irc::sockets::sockaddrs*) { DetachEvent(I_OnAcceptConnection); return MOD_RES_PASSTHRU; }
-ModResult Module::OnSendWhoLine(User*, const std::vector<std::string>&, User*, Membership*, Numeric::Numeric&) { DetachEvent(I_OnSendWhoLine); return MOD_RES_PASSTHRU; }
void Module::OnSetUserIP(LocalUser*) { DetachEvent(I_OnSetUserIP); }
void Module::OnServiceAdd(ServiceProvider&) { DetachEvent(I_OnServiceAdd); }
void Module::OnServiceDel(ServiceProvider&) { DetachEvent(I_OnServiceDel); }
ModResult Module::OnUserWrite(LocalUser*, ClientProtocol::Message&) { DetachEvent(I_OnUserWrite); return MOD_RES_PASSTHRU; }
-
-#ifdef INSPIRCD_ENABLE_TESTSUITE
-void Module::OnRunTestSuite() { }
-#endif
+ModResult Module::OnConnectionFail(LocalUser*, BufferedSocketError) { DetachEvent(I_OnConnectionFail); return MOD_RES_PASSTHRU; }
+void Module::OnShutdown(const std::string& reason) { DetachEvent(I_OnShutdown); }
ServiceProvider::ServiceProvider(Module* Creator, const std::string& Name, ServiceType Type)
: creator(Creator), name(Name), service(Type)
stdalgo::erase(*ServerInstance->Modules->NewServices, this);
}
+const char* ServiceProvider::GetTypeString() const
+{
+ switch (service)
+ {
+ case SERVICE_COMMAND:
+ return "command";
+ case SERVICE_MODE:
+ return "mode";
+ case SERVICE_METADATA:
+ return "metadata";
+ case SERVICE_IOHOOK:
+ return "iohook";
+ case SERVICE_DATA:
+ return "data service";
+ case SERVICE_CUSTOM:
+ return "module service";
+ }
+ return "unknown service";
+}
+
ModuleManager::ModuleManager()
{
}
}
/* Eh? this module doesnt exist, probably trying to set priority on an event
- * theyre not attached to.
+ * they're not attached to.
*/
return false;
bool ModuleManager::PrioritizeHooks()
{
/* 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
+ * not just the one that's loading, as the new module could affect the preference
* of others
*/
for (int tries = 0; tries < 20; tries++)
for (user_hash::const_iterator u = users.begin(); u != users.end(); )
{
User* user = u->second;
- // The module may quit the user (e.g. SSL mod unloading) and that will remove it from the container
+ // The module may quit the user (e.g. TLS (SSL) mod unloading) and that will remove it from the container
++u;
mod->OnCleanup(ExtensionItem::EXT_USER, user);
user->doUnhookExtensions(items);
}
- for(std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.begin(); i != DataProviders.end(); )
+ for (DataProviderMap::iterator i = DataProviders.begin(); i != DataProviders.end(); )
{
- std::multimap<std::string, ServiceProvider*>::iterator curr = i++;
+ DataProviderMap::iterator curr = i++;
if (curr->second->creator == mod)
+ {
DataProviders.erase(curr);
+ FOREACH_MOD(OnServiceDel, (*curr->second));
+ }
}
dynamic_reference_base::reset_all();
Modules.erase(modfind);
ServerInstance->GlobalCulls.AddItem(mod);
- ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Module %s unloaded",mod->ModuleSourceFile.c_str());
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "The %s module was unloaded", mod->ModuleSourceFile.c_str());
ServerInstance->ISupport.Build();
}
UnloadAction(Module* m) : mod(m) {}
void Call() CXX11_OVERRIDE
{
- DLLManager* dll = mod->ModuleDLLManager;
ServerInstance->Modules->DoSafeUnload(mod);
ServerInstance->GlobalCulls.Apply();
- // In pure static mode this is always NULL
- delete dll;
ServerInstance->GlobalCulls.AddItem(this);
}
};
std::map<std::string, ServiceList> servicemap;
LoadCoreModules(servicemap);
+ // Step 1: load all of the modules.
ConfigTagList tags = ServerInstance->Config->ConfTags("module");
for (ConfigIter i = tags.first; i != tags.second; ++i)
{
ConfigTag* tag = i->second;
- std::string name = ExpandModName(tag->getString("name"));
- this->NewServices = &servicemap[name];
+
+ const std::string shortname = tag->getString("name");
+ if (shortname.empty())
+ continue; // Skip malformed module tags.
// Skip modules which are already loaded.
+ const std::string name = ExpandModName(shortname);
if (Modules.find(name) != Modules.end())
continue;
+ this->NewServices = &servicemap[name];
std::cout << "[" << con_green << "*" << con_reset << "] Loading module:\t" << con_green << name << con_reset << std::endl;
if (!this->Load(name, true))
{
}
}
- ConfigStatus confstatus;
-
+ // Step 2: initialize the modules and register their services.
for (ModuleMap::const_iterator i = Modules.begin(); i != Modules.end(); ++i)
{
Module* mod = i->second;
AttachAll(mod);
AddServices(servicemap[i->first]);
mod->init();
- mod->ReadConfig(confstatus);
}
catch (CoreException& modexcept)
{
}
this->NewServices = NULL;
+ ConfigStatus confstatus(NULL, true);
+
+ // Step 3: Read the configuration for the modules. This must be done as part of
+ // its own step so that services provided by modules can be registered before
+ // the configuration is read.
+ for (ModuleMap::const_iterator i = Modules.begin(); i != Modules.end(); ++i)
+ {
+ Module* mod = i->second;
+ try
+ {
+ ServerInstance->Logs->Log("MODULE", LOG_DEBUG, "Reading configuration for %s", i->first.c_str());
+ mod->ReadConfig(confstatus);
+ }
+ catch (CoreException& modexcept)
+ {
+ LastModuleError = "Unable to read the configuration for " + mod->ModuleSourceFile + ": " + modexcept.GetReason();
+ ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
+ std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << LastModuleError << std::endl << std::endl;
+ ServerInstance->Exit(EXIT_STATUS_CONFIG);
+ }
+ }
if (!PrioritizeHooks())
ServerInstance->Exit(EXIT_STATUS_MODULE);
void ModuleManager::AddService(ServiceProvider& item)
{
+ ServerInstance->Logs->Log("SERVICE", LOG_DEBUG, "Adding %s %s provided by %s", item.name.c_str(),
+ item.GetTypeString(), item.creator ? item.creator->ModuleSourceFile.c_str() : "the core");
switch (item.service)
{
case SERVICE_DATA:
void ModuleManager::DelService(ServiceProvider& item)
{
+ ServerInstance->Logs->Log("SERVICE", LOG_DEBUG, "Deleting %s %s provided by %s", item.name.c_str(),
+ item.GetTypeString(), item.creator ? item.creator->ModuleSourceFile.c_str() : "the core");
switch (item.service)
{
case SERVICE_MODE:
case SERVICE_IOHOOK:
{
DelReferent(&item);
- return;
+ break;
}
default:
throw ModuleException("Cannot delete unknown service type");
case SERVICE_DATA:
case SERVICE_IOHOOK:
{
- std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.find(name);
+ DataProviderMap::iterator i = DataProviders.find(name);
if (i != DataProviders.end() && i->second->service == type)
return i->second;
return NULL;
std::string ModuleManager::ExpandModName(const std::string& modname)
{
- // Transform "callerid" -> "m_callerid.so" unless it already has a ".so" extension,
- // so coremods in the "core_*.so" form aren't changed
- std::string ret = modname;
- if ((modname.length() < 3) || (modname.compare(modname.size() - 3, 3, ".so")))
- ret.insert(0, "m_").append(".so");
- return ret;
+ std::string fullname;
+ if (modname.compare(0, 5, "core_") != 0 && modname.compare(0, 2, "m_") != 0)
+ fullname.append("m_");
+ fullname.append(modname);
+ if (modname.length() < 3 || modname.compare(modname.size() - 3, 3, ".so") != 0)
+ fullname.append(".so");
+ return fullname;
}
dynamic_reference_base::dynamic_reference_base(Module* Creator, const std::string& Name)
{
// Because find() may return any element with a matching key in case count(key) > 1 use lower_bound()
// to ensure a dynref with the same name as another one resolves to the same object
- std::multimap<std::string, ServiceProvider*>::iterator i = ServerInstance->Modules.DataProviders.lower_bound(name);
+ ModuleManager::DataProviderMap::iterator i = ServerInstance->Modules.DataProviders.lower_bound(name);
if ((i != ServerInstance->Modules.DataProviders.end()) && (i->first == this->name))
{
ServiceProvider* newvalue = i->second;
void ModuleManager::DelReferent(ServiceProvider* service)
{
- for (std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.begin(); i != DataProviders.end(); )
+ for (DataProviderMap::iterator i = DataProviders.begin(); i != DataProviders.end(); )
{
ServiceProvider* curr = i->second;
if (curr == service)