modulemanager.cpp

Go to the documentation of this file.
00001 /* Modular support
00002  *
00003  * (C) 2003-2013 Anope Team
00004  * Contact us at team@anope.org
00005  *
00006  * Please read COPYING and README for further details.
00007  *
00008  */
00009 
00010 #include "services.h"
00011 #include "modules.h"
00012 #include "users.h"
00013 #include "regchannel.h"
00014 
00015 #include <sys/types.h>
00016 #include <sys/stat.h>
00017 #ifndef _WIN32
00018 #include <dirent.h>
00019 #include <sys/types.h>
00020 #include <dlfcn.h>
00021 #endif
00022 
00023 std::list<Module *> ModuleManager::Modules;
00024 std::vector<Module *> ModuleManager::EventHandlers[I_END];
00025 
00026 void ModuleManager::CleanupRuntimeDirectory()
00027 {
00028         Anope::string dirbuf = Anope::DataDir + "/runtime";
00029 
00030         Log(LOG_DEBUG) << "Cleaning out Module run time directory (" << dirbuf << ") - this may take a moment please wait";
00031 
00032         DIR *dirp = opendir(dirbuf.c_str());
00033         if (!dirp)
00034         {
00035                 Log(LOG_DEBUG) << "Cannot open directory (" << dirbuf << ")";
00036                 return;
00037         }
00038         
00039         for (dirent *dp; (dp = readdir(dirp));)
00040         {
00041                 if (!dp->d_ino)
00042                         continue;
00043                 if (Anope::string(dp->d_name).equals_cs(".") || Anope::string(dp->d_name).equals_cs(".."))
00044                         continue;
00045                 Anope::string filebuf = dirbuf + "/" + dp->d_name;
00046                 unlink(filebuf.c_str());
00047         }
00048 
00049         closedir(dirp);
00050 }
00051 
00061 static ModuleReturn moduleCopyFile(const Anope::string &name, Anope::string &output)
00062 {
00063         Anope::string input = Anope::ModuleDir + "/modules/" + name + ".so";
00064         
00065         struct stat s;
00066         if (stat(input.c_str(), &s) == -1)
00067                 return MOD_ERR_NOEXIST;
00068         else if (!S_ISREG(s.st_mode))
00069                 return MOD_ERR_NOEXIST;
00070         
00071         std::ifstream source(input.c_str(), std::ios_base::in | std::ios_base::binary);
00072         if (!source.is_open())
00073                 return MOD_ERR_NOEXIST;
00074         
00075         char *tmp_output = strdup(output.c_str());
00076         int target_fd = mkstemp(tmp_output);
00077         if (target_fd == -1 || close(target_fd) == -1)
00078         {
00079                 free(tmp_output);
00080                 source.close();
00081                 return MOD_ERR_FILE_IO;
00082         }
00083         output = tmp_output;
00084         free(tmp_output);
00085 
00086         Log(LOG_DEBUG_2) << "Runtime module location: " << output;
00087         
00088         std::ofstream target(output.c_str(), std::ios_base::in | std::ios_base::binary);
00089         if (!target.is_open())
00090         {
00091                 source.close();
00092                 return MOD_ERR_FILE_IO;
00093         }
00094 
00095         int want = s.st_size;
00096         char buffer[1024];
00097         while (want > 0 && !source.fail() && !target.fail())
00098         {
00099                 source.read(buffer, std::min(want, static_cast<int>(sizeof(buffer))));
00100                 int read_len = source.gcount();
00101 
00102                 target.write(buffer, read_len);
00103                 want -= read_len;
00104         }
00105         
00106         source.close();
00107         target.close();
00108 
00109         return !source.fail() && !target.fail() ? MOD_ERR_OK : MOD_ERR_FILE_IO;
00110 }
00111 
00112 /* This code was found online at http://www.linuxjournal.com/article/3687#comment-26593
00113  *
00114  * This function will take a pointer from either dlsym or GetProcAddress and cast it in
00115  * a way that won't cause C++ warnings/errors to come up.
00116  */
00117 template <class TYPE> static TYPE function_cast(void *symbol)
00118 {
00119         union
00120         {
00121                 void *symbol;
00122                 TYPE function;
00123         } cast;
00124         cast.symbol = symbol;
00125         return cast.function;
00126 }
00127 
00128 ModuleReturn ModuleManager::LoadModule(const Anope::string &modname, User *u)
00129 {
00130         if (modname.empty())
00131                 return MOD_ERR_PARAMS;
00132 
00133         if (FindModule(modname))
00134                 return MOD_ERR_EXISTS;
00135 
00136         Log(LOG_DEBUG) << "trying to load [" << modname <<  "]";
00137 
00138         /* Generate the filename for the temporary copy of the module */
00139         Anope::string pbuf = Anope::DataDir + "/runtime/" + modname + ".so.XXXXXX";
00140 
00141         /* Don't skip return value checking! -GD */
00142         ModuleReturn ret = moduleCopyFile(modname, pbuf);
00143         if (ret != MOD_ERR_OK)
00144         {
00145                 if (ret == MOD_ERR_NOEXIST)
00146                         Log(LOG_TERMINAL) << "Error while loading " << modname << " (file does not exist)";
00147                 else if (ret == MOD_ERR_FILE_IO)
00148                         Log(LOG_TERMINAL) << "Error while loading " << modname << " (file IO error, check file permissions and diskspace)";
00149                 return ret;
00150         }
00151 
00152         dlerror();
00153         void *handle = dlopen(pbuf.c_str(), RTLD_NOW);
00154         const char *err = dlerror();
00155         if (!handle && err && *err)
00156         {
00157                 Log() << err;
00158                 return MOD_ERR_NOLOAD;
00159         }
00160 
00161         dlerror();
00162         Module *(*func)(const Anope::string &, const Anope::string &) = function_cast<Module *(*)(const Anope::string &, const Anope::string &)>(dlsym(handle, "AnopeInit"));
00163         err = dlerror();
00164         if (!func && err && *err)
00165         {
00166                 Log() << "No init function found, not an Anope module";
00167                 dlclose(handle);
00168                 return MOD_ERR_NOLOAD;
00169         }
00170 
00171         if (!func)
00172                 throw CoreException("Couldn't find constructor, yet moderror wasn't set?");
00173 
00174         /* Create module. */
00175         Anope::string nick;
00176         if (u)
00177                 nick = u->nick;
00178 
00179         Module *m;
00180 
00181         try
00182         {
00183                 m = func(modname, nick);
00184         }
00185         catch (const ModuleException &ex)
00186         {
00187                 Log() << "Error while loading " << modname << ": " << ex.GetReason();
00188                 return MOD_ERR_EXCEPTION;
00189         }
00190 
00191         m->filename = pbuf;
00192         m->handle = handle;
00193 
00194         ModuleVersion v = m->GetVersion();
00195         if (v.GetMajor() < Anope::VersionMajor() || (v.GetMajor() == Anope::VersionMajor() && v.GetMinor() < Anope::VersionMinor()))
00196         {
00197                 Log() << "Module " << modname << " is compiled against an older version of Anope " << v.GetMajor() << "." << v.GetMinor() << ", this is " << Anope::VersionShort();
00198                 DeleteModule(m);
00199                 return MOD_ERR_VERSION;
00200         }
00201         else if (v.GetMajor() > Anope::VersionMajor() || (v.GetMajor() == Anope::VersionMajor() && v.GetMinor() > Anope::VersionMinor()))
00202         {
00203                 Log() << "Module " << modname << " is compiled against a newer version of Anope " << v.GetMajor() << "." << v.GetMinor() << ", this is " << Anope::VersionShort();
00204                 DeleteModule(m);
00205                 return MOD_ERR_VERSION;
00206         }
00207         else if (v.GetPatch() < Anope::VersionPatch())
00208         {
00209                 Log() << "Module " << modname << " is compiled against an older version of Anope, " << v.GetMajor() << "." << v.GetMinor() << "." << v.GetPatch() << ", this is " << Anope::VersionShort();
00210                 DeleteModule(m);
00211                 return MOD_ERR_VERSION;
00212         }
00213         else if (v.GetPatch() > Anope::VersionPatch())
00214         {
00215                 Log() << "Module " << modname << " is compiled against a newer version of Anope, " << v.GetMajor() << "." << v.GetMinor() << "." << v.GetPatch() << ", this is " << Anope::VersionShort();
00216                 DeleteModule(m);
00217                 return MOD_ERR_VERSION;
00218         }
00219         else
00220                 Log(LOG_DEBUG_2) << "Module " << modname << " is compiled against current version of Anope " << Anope::VersionShort();
00221 
00222         Log(LOG_DEBUG) << "Module loaded.";
00223         FOREACH_MOD(I_OnModuleLoad, OnModuleLoad(u, m));
00224 
00225         return MOD_ERR_OK;
00226 }
00227 
00228 ModuleReturn ModuleManager::UnloadModule(Module *m, User *u)
00229 {
00230         if (!m)
00231                 return MOD_ERR_PARAMS;
00232 
00233         FOREACH_MOD(I_OnModuleUnload, OnModuleUnload(u, m));
00234 
00235         return DeleteModule(m);
00236 }
00237 
00238 Module *ModuleManager::FindModule(const Anope::string &name)
00239 {
00240         for (std::list<Module *>::const_iterator it = Modules.begin(), it_end = Modules.end(); it != it_end; ++it)
00241         {
00242                 Module *m = *it;
00243 
00244                 if (m->name.equals_ci(name))
00245                         return m;
00246         }
00247 
00248         return NULL;
00249 }
00250 
00251 Module *ModuleManager::FindFirstOf(ModType type)
00252 {
00253         for (std::list<Module *>::const_iterator it = Modules.begin(), it_end = Modules.end(); it != it_end; ++it)
00254         {
00255                 Module *m = *it;
00256 
00257                 if (m->type == type)
00258                         return m;
00259         }
00260 
00261         return NULL;
00262 }
00263 
00264 void ModuleManager::RequireVersion(int major, int minor, int patch)
00265 {
00266         if (Anope::VersionMajor() > major)
00267                 return;
00268         else if (Anope::VersionMajor() == major)
00269         {
00270                 if (minor == -1)
00271                         return;
00272                 else if (Anope::VersionMinor() > minor)
00273                         return;
00274                 else if (Anope::VersionMinor() == minor)
00275                 {
00276                         if (patch == -1)
00277                                 return;
00278                         else if (Anope::VersionPatch() > patch)
00279                                 return;
00280                         else if (Anope::VersionPatch() == patch)
00281                                 return;
00282                 }
00283         }
00284 
00285         throw ModuleException("This module requires version " + stringify(major) + "." + stringify(minor) + "." + stringify(patch) + " - this is " + Anope::VersionShort());
00286 }
00287 
00288 ModuleReturn ModuleManager::DeleteModule(Module *m)
00289 {
00290         if (!m || !m->handle)
00291                 return MOD_ERR_PARAMS;
00292 
00293         void *handle = m->handle;
00294         Anope::string filename = m->filename;
00295 
00296         Log(LOG_DEBUG) << "Unloading module " << m->name;
00297 
00298         dlerror();
00299         void (*destroy_func)(Module *m) = function_cast<void (*)(Module *)>(dlsym(m->handle, "AnopeFini"));
00300         const char *err = dlerror();
00301         if (!destroy_func || (err && *err))
00302         {
00303                 Log() << "No destroy function found for " << m->name << ", chancing delete...";
00304                 delete m; /* we just have to chance they haven't overwrote the delete operator then... */
00305         }
00306         else
00307                 destroy_func(m); /* Let the module delete it self, just in case */
00308 
00309         if (dlclose(handle))
00310                 Log() << dlerror();
00311 
00312         if (!filename.empty())
00313                 unlink(filename.c_str());
00314         
00315         return MOD_ERR_OK;
00316 }
00317 
00318 bool ModuleManager::Attach(Implementation i, Module *mod)
00319 {
00320         if (std::find(EventHandlers[i].begin(), EventHandlers[i].end(), mod) != EventHandlers[i].end())
00321                 return false;
00322 
00323         EventHandlers[i].push_back(mod);
00324         return true;
00325 }
00326 
00327 bool ModuleManager::Detach(Implementation i, Module *mod)
00328 {
00329         std::vector<Module *>::iterator x = std::find(EventHandlers[i].begin(), EventHandlers[i].end(), mod);
00330 
00331         if (x == EventHandlers[i].end())
00332                 return false;
00333 
00334         EventHandlers[i].erase(x);
00335         return true;
00336 }
00337 
00338 void ModuleManager::Attach(Implementation *i, Module *mod, size_t sz)
00339 {
00340         for (size_t n = 0; n < sz; ++n)
00341                 Attach(i[n], mod);
00342 }
00343 
00344 void ModuleManager::DetachAll(Module *mod)
00345 {
00346         for (size_t n = I_BEGIN + 1; n != I_END; ++n)
00347                 Detach(static_cast<Implementation>(n), mod);
00348 }
00349 
00350 bool ModuleManager::SetPriority(Module *mod, Priority s)
00351 {
00352         for (size_t n = I_BEGIN + 1; n != I_END; ++n)
00353                 SetPriority(mod, static_cast<Implementation>(n), s);
00354 
00355         return true;
00356 }
00357 
00358 bool ModuleManager::SetPriority(Module *mod, Implementation i, Priority s, Module **modules, size_t sz)
00359 {
00367         /* Locate our module. This is O(n) but it only occurs on module load so we're
00368          * not too bothered about it
00369          */
00370         size_t source = 0;
00371         bool found = false;
00372         for (size_t x = 0, end = EventHandlers[i].size(); x != end; ++x)
00373                 if (EventHandlers[i][x] == mod)
00374                 {
00375                         source = x;
00376                         found = true;
00377                         break;
00378                 }
00379 
00380         /* Eh? this module doesnt exist, probably trying to set priority on an event
00381          * theyre not attached to.
00382          */
00383         if (!found)
00384                 return false;
00385 
00386         size_t swap_pos = 0;
00387         bool swap = true;
00388         switch (s)
00389         {
00390                 /* Dummy value */
00391                 case PRIORITY_DONTCARE:
00392                         swap = false;
00393                         break;
00394                 /* Module wants to be first, sod everything else */
00395                 case PRIORITY_FIRST:
00396                         swap_pos = 0;
00397                         break;
00398                 /* Module is submissive and wants to be last... awww. */
00399                 case PRIORITY_LAST:
00400                         if (EventHandlers[i].empty())
00401                                 swap_pos = 0;
00402                         else
00403                                 swap_pos = EventHandlers[i].size() - 1;
00404                         break;
00405                 /* Place this module after a set of other modules */
00406                 case PRIORITY_AFTER:
00407                         /* Find the latest possible position */
00408                         swap_pos = 0;
00409                         swap = false;
00410                         for (size_t x = 0, end = EventHandlers[i].size(); x != end; ++x)
00411                                 for (size_t n = 0; n < sz; ++n)
00412                                         if (modules[n] && EventHandlers[i][x] == modules[n] && x >= swap_pos && source <= swap_pos)
00413                                         {
00414                                                 swap_pos = x;
00415                                                 swap = true;
00416                                         }
00417                         break;
00418                 /* Place this module before a set of other modules */
00419                 case PRIORITY_BEFORE:
00420                         swap_pos = EventHandlers[i].size() - 1;
00421                         swap = false;
00422                         for (size_t x = 0, end = EventHandlers[i].size(); x != end; ++x)
00423                                 for (size_t n = 0; n < sz; ++n)
00424                                         if (modules[n] && EventHandlers[i][x] == modules[n] && x <= swap_pos && source >= swap_pos)
00425                                         {
00426                                                 swap = true;
00427                                                 swap_pos = x;
00428                                         }
00429         }
00430 
00431         /* Do we need to swap? */
00432         if (swap && swap_pos != source)
00433         {
00434                 /* Suggestion from Phoenix, "shuffle" the modules to better retain call order */
00435                 int incrmnt = 1;
00436 
00437                 if (source > swap_pos)
00438                         incrmnt = -1;
00439 
00440                 for (unsigned j = source; j != swap_pos; j += incrmnt)
00441                 {
00442                         if (j + incrmnt > EventHandlers[i].size() - 1 || j + incrmnt < 0)
00443                                 continue;
00444 
00445                         std::swap(EventHandlers[i][j], EventHandlers[i][j + incrmnt]);
00446                 }
00447         }
00448 
00449         return true;
00450 }
00451 
00452 void ModuleManager::ClearCallBacks(Module *m)
00453 {
00454         while (!m->callbacks.empty())
00455                 delete m->callbacks.front();
00456 }
00457 
00458 void ModuleManager::UnloadAll()
00459 {
00460         std::vector<Anope::string> modules[MT_END];
00461         for (std::list<Module *>::iterator it = Modules.begin(), it_end = Modules.end(); it != it_end; ++it)
00462                 if ((*it)->type != PROTOCOL && !(*it)->GetPermanent())
00463                         modules[(*it)->type].push_back((*it)->name);
00464 
00465         for (size_t i = MT_BEGIN + 1; i != MT_END; ++i)
00466                 for (unsigned j = 0; j < modules[i].size(); ++j)
00467                 {
00468                         Module *m = FindModule(modules[i][j]);
00469                         if (m != NULL)
00470                                 UnloadModule(m, NULL);
00471                 }
00472 }
00473