mirror of
https://github.com/inspircd/inspircd.git
synced 2025-03-09 18:49:03 -04:00
Allow static build of inspircd without module support
git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@12083 e03df62e-2008-0410-955e-edbf42e46eb7
This commit is contained in:
parent
316167b917
commit
19487dbebc
@ -1696,6 +1696,28 @@ class CoreExport ModuleManager
|
||||
#define MODULE_INIT_SYM_FN_2(x) MODULE_INIT_SYM_FN_1(x)
|
||||
#define MODULE_INIT_SYM_FN_1(x) inspircd_module_ ## x
|
||||
|
||||
#ifdef PURE_STATIC
|
||||
|
||||
struct AllCommandList {
|
||||
typedef Command* (*fn)(Module*);
|
||||
AllCommandList(fn cmd);
|
||||
};
|
||||
#define COMMAND_INIT(x) static Command* MK_ ## x(Module* m) { return new x(m); } \
|
||||
static const AllCommandList PREP_ ## x(&MK_ ## x);
|
||||
|
||||
struct AllModuleList {
|
||||
typedef Module* (*fn)();
|
||||
fn init;
|
||||
std::string name;
|
||||
AllModuleList(fn mod, const std::string& Name);
|
||||
};
|
||||
|
||||
#define MODULE_INIT(x) static Module* MK_ ## x() { return new x; } \
|
||||
static const AllModuleList PREP_ ## x(&MK_ ## x, #x);
|
||||
|
||||
|
||||
#else
|
||||
|
||||
/** This definition is used as shorthand for the various classes
|
||||
* and functions needed to make a module loadable by the OS.
|
||||
* It defines the class factory and external init_module function.
|
||||
@ -1730,3 +1752,5 @@ class CoreExport ModuleManager
|
||||
#define COMMAND_INIT(c) MODULE_INIT(CommandModule<c>)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -3,7 +3,7 @@ use strict;
|
||||
use warnings;
|
||||
use POSIX qw(getcwd);
|
||||
|
||||
sub find_output($);
|
||||
sub find_output;
|
||||
sub gendep($);
|
||||
sub dep_cpp($$);
|
||||
sub dep_dir($);
|
||||
@ -28,6 +28,16 @@ sub run() {
|
||||
open MAKE, '>real.mk' or die "Could not write real.mk: $!";
|
||||
chdir "$ENV{SOURCEPATH}/src";
|
||||
|
||||
if ($ENV{PURE_STATIC}) {
|
||||
run_static();
|
||||
} else {
|
||||
run_dynamic();
|
||||
}
|
||||
close MAKE;
|
||||
}
|
||||
|
||||
sub run_dynamic() {
|
||||
my $build = $ENV{BUILDPATH};
|
||||
print MAKE <<END;
|
||||
# DO NOT EDIT THIS FILE
|
||||
# It is autogenerated by make/calcdep.pl, and will be overwritten
|
||||
@ -95,11 +105,48 @@ modules: $mods
|
||||
END
|
||||
}
|
||||
|
||||
sub find_output($) {
|
||||
my $file = shift;
|
||||
sub run_static() {
|
||||
print MAKE <<END;
|
||||
# DO NOT EDIT THIS FILE
|
||||
# It is autogenerated by make/calcdep.pl, and will be overwritten
|
||||
# every time you rerun make in the main directory
|
||||
VPATH = \$(SOURCEPATH)/src
|
||||
|
||||
bad-target:
|
||||
\@echo "This Makefile must be run by a sub-make from the source"
|
||||
\@echo "in order to set the correct environment variables"
|
||||
\@exit 1
|
||||
|
||||
all: inspircd
|
||||
|
||||
END
|
||||
my @deps;
|
||||
for my $file (<*.cpp>, <modes/*.cpp>, <socketengines/*.cpp>, <commands/*.cpp>,
|
||||
<modules/*.cpp>, <modules/m_*/*.cpp>, "threadengines/threadengine_pthread.cpp") {
|
||||
my $out = find_output $file, 1;
|
||||
dep_cpp $file, $out;
|
||||
next if $file =~ m#^socketengines/# && $file ne "socketengines/$ENV{SOCKETENGINE}.cpp";
|
||||
push @deps, $out;
|
||||
}
|
||||
|
||||
my $core_mk = join ' ', @deps;
|
||||
print MAKE <<END;
|
||||
|
||||
bin/inspircd: $core_mk
|
||||
\$(RUNCC) -o \$\@ \$(CORELDFLAGS) \$(LDLIBS) \$^ \$>
|
||||
|
||||
inspircd: bin/inspircd
|
||||
|
||||
.PHONY: all bad-target inspircd
|
||||
|
||||
END
|
||||
}
|
||||
|
||||
sub find_output {
|
||||
my($file, $static) = @_;
|
||||
my($path,$base) = $file =~ m#^((?:.*/)?)([^/]+)\.cpp# or die "Bad file $file";
|
||||
if ($path eq 'modules/' || $path eq 'commands/') {
|
||||
return "modules/$base.so";
|
||||
return $static ? "obj/$base.o" : "modules/$base.so";
|
||||
} elsif ($path eq '' || $path eq 'modes/' || $path =~ /^[a-z]+engines\/$/) {
|
||||
return "obj/$base.o";
|
||||
} elsif ($path =~ m#modules/(m_.*)/#) {
|
||||
|
@ -74,7 +74,11 @@ CXXFLAGS += -Iinclude
|
||||
RUNCC = perl $(SOURCEPATH)/make/run-cc.pl $(CC)
|
||||
@ENDIF
|
||||
|
||||
@DO_EXPORT RUNCC CXXFLAGS CC LDLIBS PICLDFLAGS VERBOSE SOCKETENGINE CORELDFLAGS
|
||||
@IFDEF PURE_STATIC
|
||||
CXXFLAGS += -DPURE_STATIC
|
||||
@ENDIF
|
||||
|
||||
@DO_EXPORT RUNCC CXXFLAGS CC LDLIBS PICLDFLAGS VERBOSE SOCKETENGINE CORELDFLAGS PURE_STATIC
|
||||
@DO_EXPORT BASE CONPATH MODPATH BINPATH SOURCEPATH BUILDPATH
|
||||
|
||||
# Default target
|
||||
|
@ -13,9 +13,6 @@
|
||||
|
||||
#include "inspircd.h"
|
||||
|
||||
#ifndef __CMD_ADMIN_H__
|
||||
#define __CMD_ADMIN_H__
|
||||
|
||||
#include "users.h"
|
||||
#include "channels.h"
|
||||
#include "ctables.h"
|
||||
@ -40,9 +37,6 @@ class CommandClearcache : public Command
|
||||
CmdResult Handle(const std::vector<std::string>& parameters, User *user);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/** Handle /CLEARCACHE
|
||||
*/
|
||||
CmdResult CommandClearcache::Handle (const std::vector<std::string>& parameters, User *user)
|
||||
|
@ -13,13 +13,6 @@
|
||||
|
||||
#include "inspircd.h"
|
||||
|
||||
#ifndef __CMD_ADMIN_H__
|
||||
#define __CMD_ADMIN_H__
|
||||
|
||||
#include "users.h"
|
||||
#include "channels.h"
|
||||
#include "ctables.h"
|
||||
|
||||
/** Handle /MODE. These command handlers can be reloaded by the core,
|
||||
* and handle basic RFC1459 commands. Commands within modules work
|
||||
* the same way, however, they can be fully unloaded, where these
|
||||
@ -40,8 +33,6 @@ class CommandMode : public Command
|
||||
CmdResult Handle(const std::vector<std::string>& parameters, User *user);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/** Handle /MODE
|
||||
*/
|
||||
|
328
src/modmanager_dynamic.cpp
Normal file
328
src/modmanager_dynamic.cpp
Normal file
@ -0,0 +1,328 @@
|
||||
/* +------------------------------------+
|
||||
* | Inspire Internet Relay Chat Daemon |
|
||||
* +------------------------------------+
|
||||
*
|
||||
* InspIRCd: (C) 2002-2009 InspIRCd Development Team
|
||||
* See: http://wiki.inspircd.org/Credits
|
||||
*
|
||||
* This program is free but copyrighted software; see
|
||||
* the file COPYING for details.
|
||||
*
|
||||
* ---------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "inspircd.h"
|
||||
#include "xline.h"
|
||||
#include "socket.h"
|
||||
#include "socketengine.h"
|
||||
#include "command_parse.h"
|
||||
#include "dns.h"
|
||||
#include "exitcodes.h"
|
||||
|
||||
#ifndef PURE_STATIC
|
||||
|
||||
bool ModuleManager::Load(const char* filename)
|
||||
{
|
||||
/* Don't allow people to specify paths for modules, it doesn't work as expected */
|
||||
if (strchr(filename, '/'))
|
||||
return false;
|
||||
/* Do we have a glob pattern in the filename?
|
||||
* The user wants to load multiple modules which
|
||||
* match the pattern.
|
||||
*/
|
||||
if (strchr(filename,'*') || (strchr(filename,'?')))
|
||||
{
|
||||
int n_match = 0;
|
||||
DIR* library = opendir(ServerInstance->Config->ModPath.c_str());
|
||||
if (library)
|
||||
{
|
||||
/* Try and locate and load all modules matching the pattern */
|
||||
dirent* entry = NULL;
|
||||
while (0 != (entry = readdir(library)))
|
||||
{
|
||||
if (InspIRCd::Match(entry->d_name, filename, ascii_case_insensitive_map))
|
||||
{
|
||||
if (!this->Load(entry->d_name))
|
||||
n_match++;
|
||||
}
|
||||
}
|
||||
closedir(library);
|
||||
}
|
||||
/* Loadmodule will now return false if any one of the modules failed
|
||||
* to load (but wont abort when it encounters a bad one) and when 1 or
|
||||
* more modules were actually loaded.
|
||||
*/
|
||||
return (n_match > 0 ? false : true);
|
||||
}
|
||||
|
||||
char modfile[MAXBUF];
|
||||
snprintf(modfile,MAXBUF,"%s/%s",ServerInstance->Config->ModPath.c_str(),filename);
|
||||
std::string filename_str = filename;
|
||||
|
||||
if (!ServerConfig::FileExists(modfile))
|
||||
{
|
||||
LastModuleError = "Module file could not be found: " + filename_str;
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Modules.find(filename_str) != Modules.end())
|
||||
{
|
||||
LastModuleError = "Module " + filename_str + " is already loaded, cannot load a module twice!";
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
|
||||
return false;
|
||||
}
|
||||
|
||||
Module* newmod = NULL;
|
||||
DLLManager* newhandle = new DLLManager(modfile);
|
||||
|
||||
try
|
||||
{
|
||||
newmod = newhandle->callInit();
|
||||
|
||||
if (newmod)
|
||||
{
|
||||
newmod->ModuleSourceFile = filename_str;
|
||||
newmod->ModuleDLLManager = newhandle;
|
||||
Version v = newmod->GetVersion();
|
||||
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (Module version %s)%s",
|
||||
filename, v.version.c_str(), (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
|
||||
|
||||
Modules[filename_str] = newmod;
|
||||
}
|
||||
else
|
||||
{
|
||||
LastModuleError = "Unable to load " + filename_str + ": " + newhandle->LastError();
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
|
||||
delete newhandle;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (CoreException& modexcept)
|
||||
{
|
||||
// failure in module constructor
|
||||
delete newmod;
|
||||
delete newhandle;
|
||||
LastModuleError = "Unable to load " + filename_str + ": " + modexcept.GetReason();
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->ModCount++;
|
||||
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
|
||||
* of others
|
||||
*/
|
||||
for(int tries = 0; tries < 20; tries++)
|
||||
{
|
||||
prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
|
||||
for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
|
||||
n->second->Prioritize();
|
||||
|
||||
if (prioritizationState == PRIO_STATE_LAST)
|
||||
break;
|
||||
if (tries == 19)
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected while loading " + filename_str);
|
||||
}
|
||||
|
||||
ServerInstance->BuildISupport();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModuleManager::CanUnload(Module* mod)
|
||||
{
|
||||
std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile);
|
||||
|
||||
if (modfind == Modules.end() || modfind->second != mod)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void ModuleManager::DoSafeUnload(Module* mod)
|
||||
{
|
||||
std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile);
|
||||
|
||||
std::vector<reference<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);
|
||||
}
|
||||
for(char m='A'; m <= 'z'; m++)
|
||||
{
|
||||
ModeHandler* mh;
|
||||
mh = ServerInstance->Modes->FindMode(m, MODETYPE_USER);
|
||||
if (mh && mh->creator == mod)
|
||||
ServerInstance->Modes->DelMode(mh);
|
||||
mh = ServerInstance->Modes->FindMode(m, MODETYPE_CHANNEL);
|
||||
if (mh && mh->creator == mod)
|
||||
ServerInstance->Modes->DelMode(mh);
|
||||
}
|
||||
|
||||
/* Tidy up any dangling resolvers */
|
||||
ServerInstance->Res->CleanResolvers(mod);
|
||||
|
||||
FOREACH_MOD(I_OnUnloadModule,OnUnloadModule(mod));
|
||||
|
||||
DetachAll(mod);
|
||||
|
||||
Modules.erase(modfind);
|
||||
ServerInstance->GlobalCulls.AddItem(mod);
|
||||
|
||||
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()
|
||||
{
|
||||
ModCount = 0;
|
||||
|
||||
printf("\nLoading core commands");
|
||||
fflush(stdout);
|
||||
|
||||
DIR* library = opendir(ServerInstance->Config->ModPath.c_str());
|
||||
if (library)
|
||||
{
|
||||
dirent* entry = NULL;
|
||||
while (0 != (entry = readdir(library)))
|
||||
{
|
||||
if (InspIRCd::Match(entry->d_name, "cmd_*.so", ascii_case_insensitive_map))
|
||||
{
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
|
||||
if (!Load(entry->d_name))
|
||||
{
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
|
||||
printf_c("\n[\033[1;31m*\033[0m] %s\n\n", this->LastError().c_str());
|
||||
ServerInstance->Exit(EXIT_STATUS_MODULE);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(library);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
ConfigTagList tags = ServerInstance->Config->ConfTags("module");
|
||||
for(ConfigIter i = tags.first; i != tags.second; ++i)
|
||||
{
|
||||
ConfigTag* tag = i->second;
|
||||
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(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());
|
||||
ServerInstance->Exit(EXIT_STATUS_MODULE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
DoSafeUnload(me->second);
|
||||
}
|
||||
}
|
||||
ServerInstance->GlobalCulls.Apply();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
127
src/modmanager_static.cpp
Normal file
127
src/modmanager_static.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
#include "inspircd.h"
|
||||
|
||||
#ifdef PURE_STATIC
|
||||
|
||||
static std::vector<AllCommandList::fn>* cmdlist = NULL;
|
||||
static std::vector<AllModuleList*>* modlist = NULL;
|
||||
|
||||
AllCommandList::AllCommandList(fn cmd)
|
||||
{
|
||||
if (!cmdlist)
|
||||
cmdlist = new std::vector<AllCommandList::fn>();
|
||||
cmdlist->push_back(cmd);
|
||||
}
|
||||
|
||||
AllModuleList::AllModuleList(AllModuleList::fn mod, const std::string& Name) : init(mod), name(Name)
|
||||
{
|
||||
if (!modlist)
|
||||
modlist = new std::vector<AllModuleList*>();
|
||||
modlist->push_back(this);
|
||||
}
|
||||
|
||||
class AllModule : public Module
|
||||
{
|
||||
std::vector<Command*> cmds;
|
||||
public:
|
||||
AllModule()
|
||||
{
|
||||
if (!cmdlist)
|
||||
return;
|
||||
try
|
||||
{
|
||||
cmds.reserve(cmdlist->size());
|
||||
for(std::vector<AllCommandList::fn>::iterator i = cmdlist->begin(); i != cmdlist->end(); ++i)
|
||||
{
|
||||
Command* c = (*i)(this);
|
||||
cmds.push_back(c);
|
||||
ServerInstance->AddCommand(c);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
this->AllModule::~AllModule();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
~AllModule()
|
||||
{
|
||||
for(std::vector<Command*>::iterator i = cmds.begin(); i != cmds.end(); ++i)
|
||||
delete *i;
|
||||
}
|
||||
|
||||
Version GetVersion()
|
||||
{
|
||||
return Version("All commands", VF_VENDOR|VF_CORE);
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(AllModule)
|
||||
|
||||
bool ModuleManager::Load(const char* filename)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModuleManager::CanUnload(Module* mod)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void ModuleManager::DoSafeUnload(Module* mod)
|
||||
{
|
||||
}
|
||||
|
||||
bool ModuleManager::Unload(Module* mod)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
|
||||
{
|
||||
callback->Call(false);
|
||||
}
|
||||
|
||||
void ModuleManager::LoadAll()
|
||||
{
|
||||
ModCount = 0;
|
||||
for(std::vector<AllModuleList*>::iterator i = modlist->begin(); i != modlist->end(); ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
Module* c = (*(**i).init)();
|
||||
c->ModuleSourceFile = (**i).name;
|
||||
Modules[(**i).name] = c;
|
||||
FOREACH_MOD(I_OnLoadModule,OnLoadModule(c));
|
||||
}
|
||||
catch (CoreException& modexcept)
|
||||
{
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT, "Unable to load " + (**i).name + ": " + modexcept.GetReason());
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
* of others
|
||||
*/
|
||||
for(int tries = 0; tries < 20; tries++)
|
||||
{
|
||||
prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
|
||||
for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
|
||||
n->second->Prioritize();
|
||||
|
||||
if (prioritizationState == PRIO_STATE_LAST)
|
||||
break;
|
||||
if (tries == 19)
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected");
|
||||
}
|
||||
|
||||
ServerInstance->BuildISupport();
|
||||
}
|
||||
|
||||
void ModuleManager::UnloadAll()
|
||||
{
|
||||
// TODO don't really need this, who cares if we leak on exit?
|
||||
}
|
||||
|
||||
#endif
|
304
src/modules.cpp
304
src/modules.cpp
@ -322,310 +322,6 @@ std::string& ModuleManager::LastError()
|
||||
return LastModuleError;
|
||||
}
|
||||
|
||||
bool ModuleManager::Load(const char* filename)
|
||||
{
|
||||
/* Don't allow people to specify paths for modules, it doesn't work as expected */
|
||||
if (strchr(filename, '/'))
|
||||
return false;
|
||||
/* Do we have a glob pattern in the filename?
|
||||
* The user wants to load multiple modules which
|
||||
* match the pattern.
|
||||
*/
|
||||
if (strchr(filename,'*') || (strchr(filename,'?')))
|
||||
{
|
||||
int n_match = 0;
|
||||
DIR* library = opendir(ServerInstance->Config->ModPath.c_str());
|
||||
if (library)
|
||||
{
|
||||
/* Try and locate and load all modules matching the pattern */
|
||||
dirent* entry = NULL;
|
||||
while (0 != (entry = readdir(library)))
|
||||
{
|
||||
if (InspIRCd::Match(entry->d_name, filename, ascii_case_insensitive_map))
|
||||
{
|
||||
if (!this->Load(entry->d_name))
|
||||
n_match++;
|
||||
}
|
||||
}
|
||||
closedir(library);
|
||||
}
|
||||
/* Loadmodule will now return false if any one of the modules failed
|
||||
* to load (but wont abort when it encounters a bad one) and when 1 or
|
||||
* more modules were actually loaded.
|
||||
*/
|
||||
return (n_match > 0 ? false : true);
|
||||
}
|
||||
|
||||
char modfile[MAXBUF];
|
||||
snprintf(modfile,MAXBUF,"%s/%s",ServerInstance->Config->ModPath.c_str(),filename);
|
||||
std::string filename_str = filename;
|
||||
|
||||
if (!ServerConfig::FileExists(modfile))
|
||||
{
|
||||
LastModuleError = "Module file could not be found: " + filename_str;
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Modules.find(filename_str) != Modules.end())
|
||||
{
|
||||
LastModuleError = "Module " + filename_str + " is already loaded, cannot load a module twice!";
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
|
||||
return false;
|
||||
}
|
||||
|
||||
Module* newmod = NULL;
|
||||
DLLManager* newhandle = new DLLManager(modfile);
|
||||
|
||||
try
|
||||
{
|
||||
newmod = newhandle->callInit();
|
||||
|
||||
if (newmod)
|
||||
{
|
||||
newmod->ModuleSourceFile = filename_str;
|
||||
newmod->ModuleDLLManager = newhandle;
|
||||
Version v = newmod->GetVersion();
|
||||
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (Module version %s)%s",
|
||||
filename, v.version.c_str(), (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
|
||||
|
||||
Modules[filename_str] = newmod;
|
||||
}
|
||||
else
|
||||
{
|
||||
LastModuleError = "Unable to load " + filename_str + ": " + newhandle->LastError();
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
|
||||
delete newhandle;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (CoreException& modexcept)
|
||||
{
|
||||
// failure in module constructor
|
||||
delete newmod;
|
||||
delete newhandle;
|
||||
LastModuleError = "Unable to load " + filename_str + ": " + modexcept.GetReason();
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->ModCount++;
|
||||
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
|
||||
* of others
|
||||
*/
|
||||
for(int tries = 0; tries < 20; tries++)
|
||||
{
|
||||
prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
|
||||
for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
|
||||
n->second->Prioritize();
|
||||
|
||||
if (prioritizationState == PRIO_STATE_LAST)
|
||||
break;
|
||||
if (tries == 19)
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected while loading " + filename_str);
|
||||
}
|
||||
|
||||
ServerInstance->BuildISupport();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModuleManager::CanUnload(Module* mod)
|
||||
{
|
||||
std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile);
|
||||
|
||||
if (modfind == Modules.end() || modfind->second != mod)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void ModuleManager::DoSafeUnload(Module* mod)
|
||||
{
|
||||
std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile);
|
||||
|
||||
std::vector<reference<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);
|
||||
}
|
||||
for(char m='A'; m <= 'z'; m++)
|
||||
{
|
||||
ModeHandler* mh;
|
||||
mh = ServerInstance->Modes->FindMode(m, MODETYPE_USER);
|
||||
if (mh && mh->creator == mod)
|
||||
ServerInstance->Modes->DelMode(mh);
|
||||
mh = ServerInstance->Modes->FindMode(m, MODETYPE_CHANNEL);
|
||||
if (mh && mh->creator == mod)
|
||||
ServerInstance->Modes->DelMode(mh);
|
||||
}
|
||||
|
||||
/* Tidy up any dangling resolvers */
|
||||
ServerInstance->Res->CleanResolvers(mod);
|
||||
|
||||
FOREACH_MOD(I_OnUnloadModule,OnUnloadModule(mod));
|
||||
|
||||
DetachAll(mod);
|
||||
|
||||
Modules.erase(modfind);
|
||||
ServerInstance->GlobalCulls.AddItem(mod);
|
||||
|
||||
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()
|
||||
{
|
||||
ModCount = 0;
|
||||
|
||||
printf("\nLoading core commands");
|
||||
fflush(stdout);
|
||||
|
||||
DIR* library = opendir(ServerInstance->Config->ModPath.c_str());
|
||||
if (library)
|
||||
{
|
||||
dirent* entry = NULL;
|
||||
while (0 != (entry = readdir(library)))
|
||||
{
|
||||
if (InspIRCd::Match(entry->d_name, "cmd_*.so", ascii_case_insensitive_map))
|
||||
{
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
|
||||
if (!Load(entry->d_name))
|
||||
{
|
||||
ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
|
||||
printf_c("\n[\033[1;31m*\033[0m] %s\n\n", this->LastError().c_str());
|
||||
ServerInstance->Exit(EXIT_STATUS_MODULE);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(library);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
ConfigTagList tags = ServerInstance->Config->ConfTags("module");
|
||||
for(ConfigIter i = tags.first; i != tags.second; ++i)
|
||||
{
|
||||
ConfigTag* tag = i->second;
|
||||
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(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());
|
||||
ServerInstance->Exit(EXIT_STATUS_MODULE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
DoSafeUnload(me->second);
|
||||
}
|
||||
}
|
||||
ServerInstance->GlobalCulls.Apply();
|
||||
}
|
||||
}
|
||||
|
||||
bool ModuleManager::PublishFeature(const std::string &FeatureName, Module* Mod)
|
||||
{
|
||||
if (Features.find(FeatureName) == Features.end())
|
||||
|
@ -70,7 +70,7 @@ class DispatcherThread;
|
||||
typedef std::map<std::string, SQLConnection*> ConnMap;
|
||||
typedef std::deque<SQLresult*> ResultQueue;
|
||||
|
||||
unsigned long count(const char * const str, char a)
|
||||
static unsigned long count(const char * const str, char a)
|
||||
{
|
||||
unsigned long n = 0;
|
||||
for (const char *p = str; *p; ++p)
|
||||
|
@ -44,7 +44,7 @@ typedef std::map<std::string, SQLConn*> ConnMap;
|
||||
*/
|
||||
enum SQLstatus { CREAD, CWRITE, WREAD, WWRITE, RREAD, RWRITE };
|
||||
|
||||
unsigned long count(const char * const str, char a)
|
||||
static unsigned long count(const char * const str, char a)
|
||||
{
|
||||
unsigned long n = 0;
|
||||
for (const char *p = str; *p; ++p)
|
||||
|
@ -31,7 +31,7 @@ typedef std::map<std::string, SQLConn*> ConnMap;
|
||||
typedef std::deque<classbase*> paramlist;
|
||||
typedef std::deque<SQLite3Result*> ResultQueue;
|
||||
|
||||
unsigned long count(const char * const str, char a)
|
||||
static unsigned long count(const char * const str, char a)
|
||||
{
|
||||
unsigned long n = 0;
|
||||
for (const char *p = str; *p; ++p)
|
||||
|
@ -457,13 +457,13 @@ class SQLhost
|
||||
|
||||
/** Overload operator== for two SQLhost objects for easy comparison.
|
||||
*/
|
||||
bool operator== (const SQLhost& l, const SQLhost& r)
|
||||
inline bool operator== (const SQLhost& l, const SQLhost& r)
|
||||
{
|
||||
return (l.id == r.id && l.host == r.host && l.port == r.port && l.name == r.name && l.user == r.user && l.pass == r.pass && l.ssl == r.ssl);
|
||||
}
|
||||
/** Overload operator!= for two SQLhost objects for easy comparison.
|
||||
*/
|
||||
bool operator!= (const SQLhost& l, const SQLhost& r)
|
||||
inline bool operator!= (const SQLhost& l, const SQLhost& r)
|
||||
{
|
||||
return (l.id != r.id || l.host != r.host || l.port != r.port || l.name != r.name || l.user != r.user || l.pass != r.pass || l.ssl != r.ssl);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user