]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modmanager_dynamic.cpp
m_showwhois Require 2 parameters for WHOISNOTICE
[user/henk/code/inspircd.git] / src / modmanager_dynamic.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *
6  * This file is part of InspIRCd.  InspIRCd is free software: you can
7  * redistribute it and/or modify it under the terms of the GNU General Public
8  * License as published by the Free Software Foundation, version 2.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19
20 #include "inspircd.h"
21 #include "xline.h"
22 #include "socket.h"
23 #include "socketengine.h"
24 #include "command_parse.h"
25 #include "dns.h"
26 #include "exitcodes.h"
27 #include <iostream>
28
29 #ifndef _WIN32
30 #include <dirent.h>
31 #endif
32
33 #ifndef PURE_STATIC
34
35 bool ModuleManager::Load(const std::string& filename, bool defer)
36 {
37         /* Don't allow people to specify paths for modules, it doesn't work as expected */
38         if (filename.find('/') != std::string::npos)
39                 return false;
40
41         char modfile[MAXBUF];
42         snprintf(modfile,MAXBUF,"%s/%s",ServerInstance->Config->ModPath.c_str(),filename.c_str());
43
44         if (!ServerConfig::FileExists(modfile))
45         {
46                 LastModuleError = "Module file could not be found: " + filename;
47                 ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
48                 return false;
49         }
50
51         if (Modules.find(filename) != Modules.end())
52         {
53                 LastModuleError = "Module " + filename + " is already loaded, cannot load a module twice!";
54                 ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
55                 return false;
56         }
57
58         Module* newmod = NULL;
59         DLLManager* newhandle = new DLLManager(modfile);
60
61         try
62         {
63                 newmod = newhandle->CallInit();
64
65                 if (newmod)
66                 {
67                         newmod->ModuleSourceFile = filename;
68                         newmod->ModuleDLLManager = newhandle;
69                         newmod->dying = false;
70                         Modules[filename] = newmod;
71                         std::string version = newhandle->GetVersion();
72                         if (defer)
73                         {
74                                 ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (Module version %s)",
75                                         filename.c_str(), version.c_str());
76                         }
77                         else
78                         {
79                                 newmod->init();
80
81                                 Version v = newmod->GetVersion();
82                                 ServerInstance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (Module version %s)%s",
83                                         filename.c_str(), version.c_str(), (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
84                         }
85                 }
86                 else
87                 {
88                         LastModuleError = "Unable to load " + filename + ": " + newhandle->LastError();
89                         ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
90                         delete newhandle;
91                         return false;
92                 }
93         }
94         catch (CoreException& modexcept)
95         {
96                 // failure in module constructor
97                 if (newmod)
98                 {
99                         DoSafeUnload(newmod);
100                         ServerInstance->GlobalCulls.AddItem(newhandle);
101                 }
102                 else
103                         delete newhandle;
104                 LastModuleError = "Unable to load " + filename + ": " + modexcept.GetReason();
105                 ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
106                 return false;
107         }
108
109         this->ModCount++;
110         if (defer)
111                 return true;
112
113         FOREACH_MOD(I_OnLoadModule,OnLoadModule(newmod));
114         /* We give every module a chance to re-prioritize when we introduce a new one,
115          * not just the one thats loading, as the new module could affect the preference
116          * of others
117          */
118         for(int tries = 0; tries < 20; tries++)
119         {
120                 prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
121                 for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
122                         n->second->Prioritize();
123
124                 if (prioritizationState == PRIO_STATE_LAST)
125                         break;
126                 if (tries == 19)
127                         ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected while loading " + filename);
128         }
129
130         ServerInstance->BuildISupport();
131         return true;
132 }
133
134 namespace {
135         struct UnloadAction : public HandlerBase0<void>
136         {
137                 Module* const mod;
138                 UnloadAction(Module* m) : mod(m) {}
139                 void Call()
140                 {
141                         DLLManager* dll = mod->ModuleDLLManager;
142                         ServerInstance->Modules->DoSafeUnload(mod);
143                         ServerInstance->GlobalCulls.Apply();
144                         delete dll;
145                         ServerInstance->GlobalCulls.AddItem(this);
146                 }
147         };
148
149         struct ReloadAction : public HandlerBase0<void>
150         {
151                 Module* const mod;
152                 HandlerBase1<void, bool>* const callback;
153                 ReloadAction(Module* m, HandlerBase1<void, bool>* c)
154                         : mod(m), callback(c) {}
155                 void Call()
156                 {
157                         DLLManager* dll = mod->ModuleDLLManager;
158                         std::string name = mod->ModuleSourceFile;
159                         ServerInstance->Modules->DoSafeUnload(mod);
160                         ServerInstance->GlobalCulls.Apply();
161                         delete dll;
162                         bool rv = ServerInstance->Modules->Load(name.c_str());
163                         if (callback)
164                                 callback->Call(rv);
165                         ServerInstance->GlobalCulls.AddItem(this);
166                 }
167         };
168 }
169
170 bool ModuleManager::Unload(Module* mod)
171 {
172         if (!CanUnload(mod))
173                 return false;
174         ServerInstance->AtomicActions.AddAction(new UnloadAction(mod));
175         return true;
176 }
177
178 void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
179 {
180         if (CanUnload(mod))
181                 ServerInstance->AtomicActions.AddAction(new ReloadAction(mod, callback));
182         else
183                 callback->Call(false);
184 }
185
186 /* We must load the modules AFTER initializing the socket engine, now */
187 void ModuleManager::LoadAll()
188 {
189         ModCount = 0;
190
191         std::cout << std::endl << "Loading core commands";
192         fflush(stdout);
193
194         DIR* library = opendir(ServerInstance->Config->ModPath.c_str());
195         if (library)
196         {
197                 dirent* entry = NULL;
198                 while (0 != (entry = readdir(library)))
199                 {
200                         if (InspIRCd::Match(entry->d_name, "cmd_*.so", ascii_case_insensitive_map))
201                         {
202                                 std::cout << ".";
203                                 fflush(stdout);
204
205                                 if (!Load(entry->d_name, true))
206                                 {
207                                         ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
208                                         std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
209                                         ServerInstance->Exit(EXIT_STATUS_MODULE);
210                                 }
211                         }
212                 }
213                 closedir(library);
214                 std::cout << std::endl;
215         }
216
217         ConfigTagList tags = ServerInstance->Config->ConfTags("module");
218         for(ConfigIter i = tags.first; i != tags.second; ++i)
219         {
220                 ConfigTag* tag = i->second;
221                 std::string name = tag->getString("name");
222                 std::cout << "[" << con_green << "*" << con_reset << "] Loading module:\t" << con_green << name << con_reset << std::endl;
223
224                 if (!this->Load(name, true))
225                 {
226                         ServerInstance->Logs->Log("MODULE", DEFAULT, this->LastError());
227                         std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
228                         ServerInstance->Exit(EXIT_STATUS_MODULE);
229                 }
230         }
231
232         for(std::map<std::string, Module*>::iterator i = Modules.begin(); i != Modules.end(); i++)
233         {
234                 Module* mod = i->second;
235                 try
236                 {
237                         ServerInstance->Logs->Log("MODULE", DEBUG, "Initializing %s", i->first.c_str());
238                         mod->init();
239                 }
240                 catch (CoreException& modexcept)
241                 {
242                         LastModuleError = "Unable to initialize " + mod->ModuleSourceFile + ": " + modexcept.GetReason();
243                         ServerInstance->Logs->Log("MODULE", DEFAULT, LastModuleError);
244                         std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << LastModuleError << std::endl << std::endl;
245                         ServerInstance->Exit(EXIT_STATUS_MODULE);
246                 }
247         }
248
249         /* We give every module a chance to re-prioritize when we introduce a new one,
250          * not just the one thats loading, as the new module could affect the preference
251          * of others
252          */
253         for(int tries = 0; tries < 20; tries++)
254         {
255                 prioritizationState = tries > 0 ? PRIO_STATE_LAST : PRIO_STATE_FIRST;
256                 for (std::map<std::string, Module*>::iterator n = Modules.begin(); n != Modules.end(); ++n)
257                         n->second->Prioritize();
258
259                 if (prioritizationState == PRIO_STATE_LAST)
260                         break;
261                 if (tries == 19)
262                 {
263                         ServerInstance->Logs->Log("MODULE", DEFAULT, "Hook priority dependency loop detected");
264                         ServerInstance->Exit(EXIT_STATUS_MODULE);
265                 }
266         }
267 }
268
269 #endif