anope

- supernets anope source code & configuration
git clone git://git.acid.vegas/anope.git
Log | Files | Refs | Archive | README

modulemanager.cpp (13473B)

      1 /* Modular support
      2  *
      3  * (C) 2003-2022 Anope Team
      4  * Contact us at team@anope.org
      5  *
      6  * Please read COPYING and README for further details.
      7  */
      8 
      9 #include "services.h"
     10 #include "modules.h"
     11 #include "users.h"
     12 #include "regchannel.h"
     13 #include "config.h"
     14 
     15 #include <sys/types.h>
     16 #include <sys/stat.h>
     17 #ifndef _WIN32
     18 #include <dirent.h>
     19 #include <sys/types.h>
     20 #include <dlfcn.h>
     21 #endif
     22 
     23 std::list<Module *> ModuleManager::Modules;
     24 std::vector<Module *> ModuleManager::EventHandlers[I_SIZE];
     25 
     26 #ifdef _WIN32
     27 void ModuleManager::CleanupRuntimeDirectory()
     28 {
     29 	Anope::string dirbuf = Anope::DataDir + "/runtime";
     30 
     31 	Log(LOG_DEBUG) << "Cleaning out Module run time directory (" << dirbuf << ") - this may take a moment, please wait";
     32 
     33 	DIR *dirp = opendir(dirbuf.c_str());
     34 	if (!dirp)
     35 	{
     36 		Log(LOG_DEBUG) << "Cannot open directory (" << dirbuf << ")";
     37 		return;
     38 	}
     39 
     40 	for (dirent *dp; (dp = readdir(dirp));)
     41 	{
     42 		if (!dp->d_ino)
     43 			continue;
     44 		if (Anope::string(dp->d_name).equals_cs(".") || Anope::string(dp->d_name).equals_cs(".."))
     45 			continue;
     46 		Anope::string filebuf = dirbuf + "/" + dp->d_name;
     47 		unlink(filebuf.c_str());
     48 	}
     49 
     50 	closedir(dirp);
     51 }
     52 
     53 /**
     54  * Copy the module from the modules folder to the runtime folder.
     55  * This will prevent module updates while the modules is loaded from
     56  * triggering a segfault, as the actual file in use will be in the
     57  * runtime folder.
     58  * @param name the name of the module to copy
     59  * @param output the destination to copy the module to
     60  * @return MOD_ERR_OK on success
     61  */
     62 static ModuleReturn moduleCopyFile(const Anope::string &name, Anope::string &output)
     63 {
     64 	Anope::string input = Anope::ModuleDir + "/modules/" + name + ".so";
     65 
     66 	struct stat s;
     67 	if (stat(input.c_str(), &s) == -1)
     68 		return MOD_ERR_NOEXIST;
     69 	else if (!S_ISREG(s.st_mode))
     70 		return MOD_ERR_NOEXIST;
     71 
     72 	std::ifstream source(input.c_str(), std::ios_base::in | std::ios_base::binary);
     73 	if (!source.is_open())
     74 		return MOD_ERR_NOEXIST;
     75 
     76 	char *tmp_output = strdup(output.c_str());
     77 	int target_fd = mkstemp(tmp_output);
     78 	if (target_fd == -1 || close(target_fd) == -1)
     79 	{
     80 		free(tmp_output);
     81 		source.close();
     82 		return MOD_ERR_FILE_IO;
     83 	}
     84 	output = tmp_output;
     85 	free(tmp_output);
     86 
     87 	Log(LOG_DEBUG_2) << "Runtime module location: " << output;
     88 
     89 	std::ofstream target(output.c_str(), std::ios_base::in | std::ios_base::binary);
     90 	if (!target.is_open())
     91 	{
     92 		source.close();
     93 		return MOD_ERR_FILE_IO;
     94 	}
     95 
     96 	int want = s.st_size;
     97 	char buffer[1024];
     98 	while (want > 0 && !source.fail() && !target.fail())
     99 	{
    100 		source.read(buffer, std::min(want, static_cast<int>(sizeof(buffer))));
    101 		int read_len = source.gcount();
    102 
    103 		target.write(buffer, read_len);
    104 		want -= read_len;
    105 	}
    106 
    107 	source.close();
    108 	target.close();
    109 
    110 	return !source.fail() && !target.fail() ? MOD_ERR_OK : MOD_ERR_FILE_IO;
    111 }
    112 #endif
    113 
    114 /* This code was found online at https://web.archive.org/web/20180318184211/https://www.linuxjournal.com/article/3687#comment-26593
    115  *
    116  * This function will take a pointer from either dlsym or GetProcAddress and cast it in
    117  * a way that won't cause C++ warnings/errors to come up.
    118  */
    119 template <class TYPE> static TYPE function_cast(void *symbol)
    120 {
    121 	union
    122 	{
    123 		void *symbol;
    124 		TYPE function;
    125 	} cast;
    126 	cast.symbol = symbol;
    127 	return cast.function;
    128 }
    129 
    130 ModuleReturn ModuleManager::LoadModule(const Anope::string &modname, User *u)
    131 {
    132 	if (modname.empty())
    133 		return MOD_ERR_PARAMS;
    134 
    135 	if (FindModule(modname))
    136 		return MOD_ERR_EXISTS;
    137 
    138 	Log(LOG_DEBUG) << "Trying to load module: " << modname;
    139 
    140 #ifdef _WIN32
    141 	/* Generate the filename for the temporary copy of the module */
    142 	Anope::string pbuf = Anope::DataDir + "/runtime/" + modname + ".so.XXXXXX";
    143 
    144 	/* Don't skip return value checking! -GD */
    145 	ModuleReturn ret = moduleCopyFile(modname, pbuf);
    146 	if (ret != MOD_ERR_OK)
    147 	{
    148 		if (ret == MOD_ERR_NOEXIST)
    149 			Log(LOG_TERMINAL) << "Error while loading " << modname << " (file does not exist)";
    150 		else if (ret == MOD_ERR_FILE_IO)
    151 			Log(LOG_TERMINAL) << "Error while loading " << modname << " (file IO error, check file permissions and diskspace)";
    152 		return ret;
    153 	}
    154 #else
    155 	Anope::string pbuf = Anope::ModuleDir + "/modules/" + modname + ".so";
    156 #endif
    157 
    158 	dlerror();
    159 	void *handle = dlopen(pbuf.c_str(), RTLD_NOW);
    160 	const char *err = dlerror();
    161 	if (!handle)
    162 	{
    163 		if (err && *err)
    164 			Log() << err;
    165 		return MOD_ERR_NOLOAD;
    166 	}
    167 
    168 	try
    169 	{
    170 		ModuleVersion v = GetVersion(handle);
    171 
    172 		if (v.GetMajor() < Anope::VersionMajor() || (v.GetMajor() == Anope::VersionMajor() && v.GetMinor() < Anope::VersionMinor()))
    173 		{
    174 			Log() << "Module " << modname << " is compiled against an older version of Anope " << v.GetMajor() << "." << v.GetMinor() << ", this is " << Anope::VersionShort();
    175 			dlclose(handle);
    176 			return MOD_ERR_VERSION;
    177 		}
    178 		else if (v.GetMajor() > Anope::VersionMajor() || (v.GetMajor() == Anope::VersionMajor() && v.GetMinor() > Anope::VersionMinor()))
    179 		{
    180 			Log() << "Module " << modname << " is compiled against a newer version of Anope " << v.GetMajor() << "." << v.GetMinor() << ", this is " << Anope::VersionShort();
    181 			dlclose(handle);
    182 			return MOD_ERR_VERSION;
    183 		}
    184 		else if (v.GetPatch() < Anope::VersionPatch())
    185 		{
    186 			Log() << "Module " << modname << " is compiled against an older version of Anope, " << v.GetMajor() << "." << v.GetMinor() << "." << v.GetPatch() << ", this is " << Anope::VersionShort();
    187 			dlclose(handle);
    188 			return MOD_ERR_VERSION;
    189 		}
    190 		else if (v.GetPatch() > Anope::VersionPatch())
    191 		{
    192 			Log() << "Module " << modname << " is compiled against a newer version of Anope, " << v.GetMajor() << "." << v.GetMinor() << "." << v.GetPatch() << ", this is " << Anope::VersionShort();
    193 			dlclose(handle);
    194 			return MOD_ERR_VERSION;
    195 		}
    196 		else
    197 		{
    198 			Log(LOG_DEBUG_2) << "Module " << modname << " is compiled against current version of Anope " << Anope::VersionShort();
    199 		}
    200 	}
    201 	catch (const ModuleException &ex)
    202 	{
    203 		/* this error has already been logged */
    204 		dlclose(handle);
    205 		return MOD_ERR_NOLOAD;
    206 	}
    207 
    208 	dlerror();
    209 	Module *(*func)(const Anope::string &, const Anope::string &) = function_cast<Module *(*)(const Anope::string &, const Anope::string &)>(dlsym(handle, "AnopeInit"));
    210 	err = dlerror();
    211 	if (!func)
    212 	{
    213 		Log() << "No init function found, not an Anope module";
    214 		if (err && *err)
    215 			Log(LOG_DEBUG) << err;
    216 		dlclose(handle);
    217 		return MOD_ERR_NOLOAD;
    218 	}
    219 
    220 	/* Create module. */
    221 	Anope::string nick;
    222 	if (u)
    223 		nick = u->nick;
    224 
    225 	Module *m;
    226 
    227 	ModuleReturn moderr = MOD_ERR_OK;
    228 	try
    229 	{
    230 		m = func(modname, nick);
    231 	}
    232 	catch (const ModuleException &ex)
    233 	{
    234 		Log() << "Error while loading " << modname << ": " << ex.GetReason();
    235 		moderr = MOD_ERR_EXCEPTION;
    236 	}
    237 
    238 	if (moderr != MOD_ERR_OK)
    239 	{
    240 		if (dlclose(handle))
    241 			Log() << dlerror();
    242 		return moderr;
    243 	}
    244 
    245 	m->filename = pbuf;
    246 	m->handle = handle;
    247 
    248 	/* Initialize config */
    249 	try
    250 	{
    251 		m->OnReload(Config);
    252 	}
    253 	catch (const ModuleException &ex)
    254 	{
    255 		Log() << "Module " << modname << " couldn't load:" << ex.GetReason();
    256 		moderr = MOD_ERR_EXCEPTION;
    257 	}
    258 	catch (const ConfigException &ex)
    259 	{
    260 		Log() << "Module " << modname << " couldn't load due to configuration problems: " << ex.GetReason();
    261 		moderr = MOD_ERR_EXCEPTION;
    262 	}
    263 	catch (const NotImplementedException &ex)
    264 	{
    265 	}
    266 
    267 	if (moderr != MOD_ERR_OK)
    268 	{
    269 		DeleteModule(m);
    270 		return moderr;
    271 	}
    272 
    273 	Log(LOG_DEBUG) << "Module " << modname << " loaded.";
    274 
    275 	/* Attach module to all events */
    276 	for (unsigned i = 0; i < I_SIZE; ++i)
    277 		EventHandlers[i].push_back(m);
    278 
    279 	m->Prioritize();
    280 
    281 	FOREACH_MOD(OnModuleLoad, (u, m));
    282 
    283 	return MOD_ERR_OK;
    284 }
    285 
    286 ModuleVersion ModuleManager::GetVersion(void *handle)
    287 {
    288 	dlerror();
    289 	ModuleVersionC (*func)() = function_cast<ModuleVersionC (*)()>(dlsym(handle, "AnopeVersion"));;
    290 	if (!func)
    291 	{
    292 		Log() << "No version function found, not an Anope module";
    293 
    294 		const char *err = dlerror();
    295 		if (err && *err)
    296 			Log(LOG_DEBUG) << err;
    297 
    298 		throw ModuleException("No version");
    299 	}
    300 
    301 	return func();
    302 }
    303 
    304 ModuleReturn ModuleManager::UnloadModule(Module *m, User *u)
    305 {
    306 	if (!m)
    307 		return MOD_ERR_PARAMS;
    308 
    309 	FOREACH_MOD(OnModuleUnload, (u, m));
    310 
    311 	return DeleteModule(m);
    312 }
    313 
    314 Module *ModuleManager::FindModule(const Anope::string &name)
    315 {
    316 	for (std::list<Module *>::const_iterator it = Modules.begin(), it_end = Modules.end(); it != it_end; ++it)
    317 	{
    318 		Module *m = *it;
    319 
    320 		if (m->name.equals_ci(name))
    321 			return m;
    322 	}
    323 
    324 	return NULL;
    325 }
    326 
    327 Module *ModuleManager::FindFirstOf(ModType type)
    328 {
    329 	for (std::list<Module *>::const_iterator it = Modules.begin(), it_end = Modules.end(); it != it_end; ++it)
    330 	{
    331 		Module *m = *it;
    332 
    333 		if (m->type & type)
    334 			return m;
    335 	}
    336 
    337 	return NULL;
    338 }
    339 
    340 void ModuleManager::RequireVersion(int major, int minor, int patch)
    341 {
    342 	if (Anope::VersionMajor() > major)
    343 		return;
    344 	else if (Anope::VersionMajor() == major)
    345 	{
    346 		if (minor == -1)
    347 			return;
    348 		else if (Anope::VersionMinor() > minor)
    349 			return;
    350 		else if (Anope::VersionMinor() == minor)
    351 		{
    352 			if (patch == -1)
    353 				return;
    354 			else if (Anope::VersionPatch() > patch)
    355 				return;
    356 			else if (Anope::VersionPatch() == patch)
    357 				return;
    358 		}
    359 	}
    360 
    361 	throw ModuleException("This module requires version " + stringify(major) + "." + stringify(minor) + "." + stringify(patch) + " - this is " + Anope::VersionShort());
    362 }
    363 
    364 ModuleReturn ModuleManager::DeleteModule(Module *m)
    365 {
    366 	if (!m || !m->handle)
    367 		return MOD_ERR_PARAMS;
    368 
    369 	void *handle = m->handle;
    370 	Anope::string filename = m->filename;
    371 
    372 	Log(LOG_DEBUG) << "Unloading module " << m->name;
    373 
    374 	dlerror();
    375 	void (*destroy_func)(Module *m) = function_cast<void (*)(Module *)>(dlsym(m->handle, "AnopeFini"));
    376 	const char *err = dlerror();
    377 	if (!destroy_func || (err && *err))
    378 	{
    379 		Log() << "No destroy function found for " << m->name << ", chancing delete...";
    380 		delete m; /* we just have to chance they haven't overwrote the delete operator then... */
    381 	}
    382 	else
    383 		destroy_func(m); /* Let the module delete it self, just in case */
    384 
    385 	if (dlclose(handle))
    386 		Log() << dlerror();
    387 
    388 #ifdef _WIN32
    389 	if (!filename.empty())
    390 		unlink(filename.c_str());
    391 #endif
    392 
    393 	return MOD_ERR_OK;
    394 }
    395 
    396 void ModuleManager::DetachAll(Module *mod)
    397 {
    398 	for (unsigned i = 0; i < I_SIZE; ++i)
    399 	{
    400 		std::vector<Module *> &mods = EventHandlers[i];
    401 		std::vector<Module *>::iterator it2 = std::find(mods.begin(), mods.end(), mod);
    402 		if (it2 != mods.end())
    403 			mods.erase(it2);
    404 	}
    405 }
    406 
    407 bool ModuleManager::SetPriority(Module *mod, Priority s)
    408 {
    409 	for (unsigned i = 0; i < I_SIZE; ++i)
    410 		SetPriority(mod, static_cast<Implementation>(i), s);
    411 
    412 	return true;
    413 }
    414 
    415 bool ModuleManager::SetPriority(Module *mod, Implementation i, Priority s, Module **modules, size_t sz)
    416 {
    417 	/** To change the priority of a module, we first find its position in the vector,
    418 	 * then we find the position of the other modules in the vector that this module
    419 	 * wants to be before/after. We pick off either the first or last of these depending
    420 	 * on which they want, and we make sure our module is *at least* before or after
    421 	 * the first or last of this subset, depending again on the type of priority.
    422 	 */
    423 
    424 	/* Locate our module. This is O(n) but it only occurs on module load so we're
    425 	 * not too bothered about it
    426 	 */
    427 	size_t source = 0;
    428 	bool found = false;
    429 	for (size_t x = 0, end = EventHandlers[i].size(); x != end; ++x)
    430 		if (EventHandlers[i][x] == mod)
    431 		{
    432 			source = x;
    433 			found = true;
    434 			break;
    435 		}
    436 
    437 	/* Eh? this module doesn't exist, probably trying to set priority on an event
    438 	 * they're not attached to.
    439 	 */
    440 	if (!found)
    441 		return false;
    442 
    443 	size_t swap_pos = 0;
    444 	bool swap = true;
    445 	switch (s)
    446 	{
    447 		/* Dummy value */
    448 		case PRIORITY_DONTCARE:
    449 			swap = false;
    450 			break;
    451 		/* Module wants to be first, sod everything else */
    452 		case PRIORITY_FIRST:
    453 			swap_pos = 0;
    454 			break;
    455 		/* Module is submissive and wants to be last... awww. */
    456 		case PRIORITY_LAST:
    457 			if (EventHandlers[i].empty())
    458 				swap_pos = 0;
    459 			else
    460 				swap_pos = EventHandlers[i].size() - 1;
    461 			break;
    462 		/* Place this module after a set of other modules */
    463 		case PRIORITY_AFTER:
    464 			/* Find the latest possible position */
    465 			swap_pos = 0;
    466 			swap = false;
    467 			for (size_t x = 0, end = EventHandlers[i].size(); x != end; ++x)
    468 				for (size_t n = 0; n < sz; ++n)
    469 					if (modules[n] && EventHandlers[i][x] == modules[n] && x >= swap_pos && source <= swap_pos)
    470 					{
    471 						swap_pos = x;
    472 						swap = true;
    473 					}
    474 			break;
    475 		/* Place this module before a set of other modules */
    476 		case PRIORITY_BEFORE:
    477 			swap_pos = EventHandlers[i].size() - 1;
    478 			swap = false;
    479 			for (size_t x = 0, end = EventHandlers[i].size(); x != end; ++x)
    480 				for (size_t n = 0; n < sz; ++n)
    481 					if (modules[n] && EventHandlers[i][x] == modules[n] && x <= swap_pos && source >= swap_pos)
    482 					{
    483 						swap = true;
    484 						swap_pos = x;
    485 					}
    486 	}
    487 
    488 	/* Do we need to swap? */
    489 	if (swap && swap_pos != source)
    490 	{
    491 		/* Suggestion from Phoenix, "shuffle" the modules to better retain call order */
    492 		int increment = 1;
    493 
    494 		if (source > swap_pos)
    495 			increment = -1;
    496 
    497 		for (unsigned j = source; j != swap_pos; j += increment)
    498 		{
    499 			if (j + increment > EventHandlers[i].size() - 1 || (!j && increment == -1))
    500 				continue;
    501 
    502 			std::swap(EventHandlers[i][j], EventHandlers[i][j + increment]);
    503 		}
    504 	}
    505 
    506 	return true;
    507 }
    508 
    509 void ModuleManager::UnloadAll()
    510 {
    511 	std::vector<Anope::string> modules;
    512 	for (size_t i = 1, j = 0; i != MT_END; j |= i, i <<= 1)
    513 		for (std::list<Module *>::iterator it = Modules.begin(), it_end = Modules.end(); it != it_end; ++it)
    514 		{
    515 			Module *m = *it;
    516 			if ((m->type & j) == m->type)
    517 				modules.push_back(m->name);
    518 		}
    519 
    520 	for (unsigned i = 0; i < modules.size(); ++i)
    521 	{
    522 		Module *m = FindModule(modules[i]);
    523 		if (m != NULL)
    524 			UnloadModule(m, NULL);
    525 	}
    526 }