]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modmanager_dynamic.cpp
b4a1d5906f22801c0a840ebb51c493ac3f86d205
[user/henk/code/inspircd.git] / src / modmanager_dynamic.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "xline.h"
16 #include "socket.h"
17 #include "socketengine.h"
18 #include "command_parse.h"
19 #include "dns.h"
20 #include "exitcodes.h"
21
22 #ifndef PURE_STATIC
23
24 bool ModuleManager::Load(const char* filename)
25 {
26         /* Don't allow people to specify paths for modules, it doesn't work as expected */
27         if (strchr(filename, '/'))
28                 return false;
29         /* Do we have a glob pattern in the filename?
30          * The user wants to load multiple modules which
31          * match the pattern.
32          */
33         if (strchr(filename,'*') || (strchr(filename,'?')))
34         {
35                 int n_match = 0;
36                 DIR* library = opendir(ServerInstance->Config->ModPath.c_str());
37                 if (library)
38                 {
39                         /* Try and locate and load all modules matching the pattern */
40                         dirent* entry = NULL;
41                         while (0 != (entry = readdir(library)))
42                         {
43                                 if (InspIRCd::Match(entry->d_name, filename, ascii_case_insensitive_map))
44                                 {
45                                         if (!this->Load(entry->d_name))
46                                                 n_match++;
47                                 }
48                         }
49                         closedir(library);
50                 }
51                 /* Loadmodule will now return false if any one of the modules failed
52                  * to load (but wont abort when it encounters a bad one) and when 1 or
53                  * more modules were actually loaded.
54                  */
55                 return (n_match > 0 ? false : true);
56         }
57
58         char modfile[MAXBUF];
59         snprintf(modfile,MAXBUF,"%s/%s",ServerInstance->Config->ModPath.c_str(),filename);
60         std::string filename_str = filename;
61
62         if (!ServerConfig::FileExists(modfile))
63         {
64                 LastModuleError = "Module file could not be found: " + filename_str;
65                 ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
66                 return false;
67         }
68
69         if (Modules.find(filename_str) != Modules.end())
70         {
71                 LastModuleError = "Module " + filename_str + " is already loaded, cannot load a module twice!";
72                 ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
73                 return false;
74         }
75
76         Module* newmod = NULL;
77         DLLManager* newhandle = new DLLManager(modfile);
78
79         try
80         {
81                 newmod = newhandle->callInit();
82
83                 if (newmod)
84                 {
85                         newmod->ModuleSourceFile = filename_str;
86                         newmod->ModuleDLLManager = newhandle;
87                         Version v = newmod->GetVersion();
88
89                         ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (Module version %s)%s",
90                                 filename, v.version.c_str(), (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
91
92                         Modules[filename_str] = newmod;
93                 }
94                 else
95                 {
96                         LastModuleError = "Unable to load " + filename_str + ": " + newhandle->LastError();
97                         ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
98                         delete newhandle;
99                         return false;
100                 }
101         }
102         catch (CoreException& modexcept)
103         {
104                 // failure in module constructor
105                 delete newmod;
106                 delete newhandle;
107                 LastModuleError = "Unable to load " + filename_str + ": " + modexcept.GetReason();
108                 ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
109                 return false;
110         }
111
112         this->ModCount++;
113         FOREACH_MOD(I_OnLoadModule,OnLoadModule(newmod));
114
115         /* We give every module a chance to re-prioritize when we introduce a new one,
116          * not just the one thats loading, as the new module could affect the preference
117          * of others
118          */
119         for(int tries = 0; tries < 20; tries++)
120         {
121                 prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
122                 for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
123                         n->second->Prioritize();
124
125                 if (prioritizationState == PRIO_STATE_LAST)
126                         break;
127                 if (tries == 19)
128                         ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected while loading " + filename_str);
129         }
130
131         ServerInstance->BuildISupport();
132         return true;
133 }
134
135 bool ModuleManager::CanUnload(Module* mod)
136 {
137         std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile);
138
139         if (modfind == Modules.end() || modfind->second != mod)
140         {
141                 LastModuleError = "Module " + mod->ModuleSourceFile + " is not loaded, cannot unload it!";
142                 ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
143                 return false;
144         }
145         if (mod->GetVersion().Flags & VF_STATIC)
146         {
147                 LastModuleError = "Module " + mod->ModuleSourceFile + " not unloadable (marked static)";
148                 ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
149                 return false;
150         }
151         std::pair<int,std::string> intercount = GetInterfaceInstanceCount(mod);
152         if (intercount.first > 0)
153         {
154                 LastModuleError = "Failed to unload module " + mod->ModuleSourceFile + ", being used by " + ConvToStr(intercount.first) + " other(s) via interface '" + intercount.second + "'";
155                 ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
156                 return false;
157         }
158         return true;
159 }
160
161 void ModuleManager::DoSafeUnload(Module* mod)
162 {
163         std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile);
164
165         std::vector<reference<ExtensionItem> > items;
166         ServerInstance->Extensions.BeginUnregister(modfind->second, items);
167         /* Give the module a chance to tidy out all its metadata */
168         for (chan_hash::iterator c = ServerInstance->chanlist->begin(); c != ServerInstance->chanlist->end(); c++)
169         {
170                 mod->OnCleanup(TYPE_CHANNEL,c->second);
171                 c->second->doUnhookExtensions(items);
172                 const UserMembList* users = c->second->GetUsers();
173                 for(UserMembCIter mi = users->begin(); mi != users->end(); mi++)
174                         mi->second->doUnhookExtensions(items);
175         }
176         for (user_hash::iterator u = ServerInstance->Users->clientlist->begin(); u != ServerInstance->Users->clientlist->end(); u++)
177         {
178                 mod->OnCleanup(TYPE_USER,u->second);
179                 u->second->doUnhookExtensions(items);
180         }
181         for(char m='A'; m <= 'z'; m++)
182         {
183                 ModeHandler* mh;
184                 mh = ServerInstance->Modes->FindMode(m, MODETYPE_USER);
185                 if (mh && mh->creator == mod)
186                         ServerInstance->Modes->DelMode(mh);
187                 mh = ServerInstance->Modes->FindMode(m, MODETYPE_CHANNEL);
188                 if (mh && mh->creator == mod)
189                         ServerInstance->Modes->DelMode(mh);
190         }
191
192         /* Tidy up any dangling resolvers */
193         ServerInstance->Res->CleanResolvers(mod);
194
195         FOREACH_MOD(I_OnUnloadModule,OnUnloadModule(mod));
196
197         DetachAll(mod);
198
199         Modules.erase(modfind);
200         ServerInstance->GlobalCulls.AddItem(mod);
201
202         ServerInstance->Logs->Log("MODULE", DEFAULT,"Module %s unloaded",mod->ModuleSourceFile.c_str());
203         this->ModCount--;
204         ServerInstance->BuildISupport();
205 }
206
207 namespace {
208         struct UnloadAction : public HandlerBase0<void>
209         {
210                 Module* const mod;
211                 UnloadAction(Module* m) : mod(m) {}
212                 void Call()
213                 {
214                         DLLManager* dll = mod->ModuleDLLManager;
215                         ServerInstance->Modules->DoSafeUnload(mod);
216                         ServerInstance->GlobalCulls.Apply();
217                         delete dll;
218                         ServerInstance->GlobalCulls.AddItem(this);
219                 }
220         };
221
222         struct ReloadAction : public HandlerBase0<void>
223         {
224                 Module* const mod;
225                 HandlerBase1<void, bool>* const callback;
226                 ReloadAction(Module* m, HandlerBase1<void, bool>* c)
227                         : mod(m), callback(c) {}
228                 void Call()
229                 {
230                         DLLManager* dll = mod->ModuleDLLManager;
231                         std::string name = mod->ModuleSourceFile;
232                         ServerInstance->Modules->DoSafeUnload(mod);
233                         ServerInstance->GlobalCulls.Apply();
234                         delete dll;
235                         bool rv = ServerInstance->Modules->Load(name.c_str());
236                         callback->Call(rv);
237                         ServerInstance->GlobalCulls.AddItem(this);
238                 }
239         };
240 }
241
242 bool ModuleManager::Unload(Module* mod)
243 {
244         if (!CanUnload(mod))
245                 return false;
246         ServerInstance->AtomicActions.AddAction(new UnloadAction(mod));
247         return true;
248 }
249
250 void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
251 {
252         if (CanUnload(mod))
253                 ServerInstance->AtomicActions.AddAction(new ReloadAction(mod, callback));
254         else
255                 callback->Call(false);
256 }
257
258 /* We must load the modules AFTER initializing the socket engine, now */
259 void ModuleManager::LoadAll()
260 {
261         ModCount = 0;
262
263         printf("\nLoading core commands");
264         fflush(stdout);
265
266         DIR* library = opendir(ServerInstance->Config->ModPath.c_str());
267         if (library)
268         {
269                 dirent* entry = NULL;
270                 while (0 != (entry = readdir(library)))
271                 {
272                         if (InspIRCd::Match(entry->d_name, "cmd_*.so", ascii_case_insensitive_map))
273                         {
274                                 printf(".");
275                                 fflush(stdout);
276
277                                 if (!Load(entry->d_name))
278                                 {
279                                         ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
280                                         printf_c("\n[\033[1;31m*\033[0m] %s\n\n", this->LastError().c_str());
281                                         ServerInstance->Exit(EXIT_STATUS_MODULE);
282                                 }
283                         }
284                 }
285                 closedir(library);
286                 printf("\n");
287         }
288
289         ConfigTagList tags = ServerInstance->Config->ConfTags("module");
290         for(ConfigIter i = tags.first; i != tags.second; ++i)
291         {
292                 ConfigTag* tag = i->second;
293                 std::string name = tag->getString("name");
294                 printf_c("[\033[1;32m*\033[0m] Loading module:\t\033[1;32m%s\033[0m\n",name.c_str());
295
296                 if (!this->Load(name.c_str()))
297                 {
298                         ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
299                         printf_c("\n[\033[1;31m*\033[0m] %s\n\n", this->LastError().c_str());
300                         ServerInstance->Exit(EXIT_STATUS_MODULE);
301                 }
302         }
303 }
304
305 void ModuleManager::UnloadAll()
306 {
307         /* We do this more than once, so that any service providers get a
308          * chance to be unhooked by the modules using them, but then get
309          * a chance to be removed themsleves.
310          *
311          * Note: this deliberately does NOT delete the DLLManager objects
312          */
313         for (int tries = 0; tries < 4; tries++)
314         {
315                 std::map<std::string, Module*>::iterator i = Modules.begin();
316                 while (i != Modules.end())
317                 {
318                         std::map<std::string, Module*>::iterator me = i++;
319                         if (CanUnload(me->second))
320                         {
321                                 DoSafeUnload(me->second);
322                         }
323                 }
324                 ServerInstance->GlobalCulls.Apply();
325         }
326 }
327
328 #endif