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