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 }