]>
Commit | Line | Data |
---|---|---|
2c5db955 CP |
1 | /* |
2 | * modules.c: | |
3 | * | |
4 | * Provides functions for dealing with dynamic modules. | |
5 | */ | |
6 | ||
7 | #include <stdlib.h> | |
8 | #include <dlfcn.h> | |
9 | #include "modules.h" | |
10 | #include "../lib/array.h" | |
11 | #include "../lib/sstring.h" | |
12 | #include "../lib/irc_string.h" | |
3fa581ac | 13 | #include "../lib/splitline.h" |
2c5db955 CP |
14 | #include "config.h" |
15 | #include "error.h" | |
16 | #include <stdio.h> | |
17 | #include <string.h> | |
3f1c5f43 CP |
18 | #include <errno.h> |
19 | #include <unistd.h> | |
2c5db955 | 20 | |
3fa581ac | 21 | #define DEPFILE "modules.dep" |
22 | ||
23 | #define MAXMODULES 100 | |
24 | ||
25 | /* Module dependency stuff. | |
26 | * | |
27 | * "parents" are modules we rely on in order to work. | |
28 | * Whenever we load a module we must ensure all parents are present first. | |
29 | * | |
30 | * "children" are modules which rely on us. | |
31 | * Whenever we remove a module, we must ensure all children are removed. | |
32 | * If it's a reload, we will reload them afterwards. | |
33 | * | |
34 | * Now, depmod.pl cunningly arranges the file so that each modules' parents are | |
35 | * always defined earlier in the file. So we can allocate and fill in the parents | |
36 | * array as we read the file. Note that we can also arrange for numchildren to be | |
37 | * correct on each node when we've finished. | |
38 | * | |
39 | * We then do a second pass. At each node, we allocate the space for the | |
40 | * children array and reset numchildren to 0. We can also then populate our | |
41 | * parents' children array with ourselves, using "numchildren" to get the | |
42 | * list in order. | |
43 | * | |
44 | * At the end of the second pass we should have a correctly filled in array! | |
45 | */ | |
46 | ||
47 | struct module_dep { | |
48 | sstring *name; | |
49 | unsigned int numparents; | |
50 | unsigned int numchildren; | |
51 | struct module_dep **parents; | |
52 | struct module_dep **children; | |
53 | unsigned int reloading; | |
54 | }; | |
55 | ||
2c5db955 CP |
56 | array modules; |
57 | ||
3fa581ac | 58 | struct module_dep moduledeps[MAXMODULES]; |
59 | unsigned int knownmodules=0; | |
60 | ||
2c5db955 CP |
61 | sstring *moddir; |
62 | sstring *modsuffix; | |
63 | ||
3fa581ac | 64 | /* Clear out and free the dependencies. */ |
65 | void clearmoduledeps() { | |
66 | unsigned int i; | |
67 | ||
68 | for (i=0;i<knownmodules;i++) { | |
69 | free(moduledeps[i].parents); | |
70 | free(moduledeps[i].children); | |
71 | } | |
72 | ||
73 | knownmodules=0; | |
74 | } | |
75 | ||
76 | /* Get a pointer to the given module's dependency record. */ | |
77 | struct module_dep *getmoduledep(const char *name) { | |
78 | unsigned int i; | |
79 | ||
80 | for (i=0;i<knownmodules;i++) { | |
81 | if (!strcmp(name,moduledeps[i].name->content)) | |
82 | return &(moduledeps[i]); | |
83 | } | |
84 | ||
85 | return NULL; | |
86 | } | |
87 | ||
88 | /* Populate the dependency array. Read the monster description above for details. | |
89 | * This relies on moddir being set, so call this after setting it up. */ | |
90 | void initmoduledeps() { | |
91 | FILE *fp; | |
92 | char buf[1024]; | |
93 | char *largv[100]; | |
94 | char largc; | |
95 | char *ch; | |
96 | struct module_dep *mdp, *tmdp, *mdps; | |
97 | unsigned int i,j; | |
98 | ||
99 | sprintf(buf,"%s/%s",moddir->content,DEPFILE); | |
100 | ||
101 | if (!(fp=fopen(buf,"r"))) { | |
102 | Error("core",ERR_WARNING,"Unable to open module dependency file: %s",buf); | |
103 | } else { | |
104 | /* First pass */ | |
105 | while (!feof(fp)) { | |
106 | fgets(buf, sizeof(buf), fp); | |
107 | if (feof(fp)) | |
108 | break; | |
109 | ||
110 | /* Chomp off that ruddy newline. */ | |
111 | for (ch=buf;*ch;ch++) { | |
112 | if (*ch=='\n' || *ch=='\r') { | |
113 | *ch='\0'; | |
114 | break; | |
115 | } | |
116 | } | |
117 | ||
118 | /* We have a space-delimited list of things. Whatever will we do with that? :) */ | |
119 | largc=splitline(buf,largv,100,0); | |
120 | ||
121 | if (largc<1) | |
122 | continue; | |
123 | ||
124 | /* Add us to the array */ | |
125 | i=knownmodules++; | |
126 | if (i>=MAXMODULES) { | |
127 | Error("core",ERR_FATAL,"Too many modules in dependency file; rebuild with higher MAXMODULES.\n"); | |
128 | return; | |
129 | } | |
130 | ||
131 | mdp=&(moduledeps[i]); | |
132 | ||
133 | mdp->name=getsstring(largv[0],100); | |
134 | mdp->numparents=largc-1; | |
135 | mdp->numchildren=0; /* peace, for now */ | |
136 | mdp->parents=malloc(mdp->numparents * sizeof(struct module_dep *)); | |
137 | ||
138 | /* Fill in the parents array */ | |
139 | for (i=0;i<(largc-1);i++) { | |
140 | if (!(mdp->parents[i]=getmoduledep(largv[i+1]))) { | |
141 | Error("core",ERR_WARNING,"Couldn't find parent module %s of %s.",largv[i+1],largv[0]); | |
142 | continue; | |
143 | } | |
144 | mdp->parents[i]->numchildren++; /* break the bad news */ | |
145 | } | |
146 | } | |
147 | ||
148 | /* Second pass */ | |
149 | for (i=0;i<knownmodules;i++) { | |
150 | mdp=&(moduledeps[i]); | |
151 | ||
152 | /* Allocate child array */ | |
153 | if (mdp->numchildren) { | |
154 | mdp->children=malloc(mdp->numchildren * sizeof(struct module_dep *)); | |
155 | mdp->numchildren=0; /* if only real life were this simple */ | |
156 | } | |
157 | ||
158 | /* Now fill in the children arrays of our parents (bear with me on this) */ | |
159 | for (j=0;j<mdp->numparents;j++) { | |
160 | tmdp=mdp->parents[j]; /* This is just... */ | |
161 | ||
162 | tmdp->children[tmdp->numchildren++]=mdp; /* ... to give this line a chance at making sense */ | |
163 | } | |
164 | } | |
165 | } | |
166 | } | |
167 | ||
2c5db955 CP |
168 | void modulerehash() { |
169 | int i; | |
170 | sstring **mods; | |
171 | array *autoloads; | |
172 | ||
173 | if (moddir!=NULL) | |
174 | freesstring(moddir); | |
175 | ||
176 | if (modsuffix!=NULL) | |
177 | freesstring(modsuffix); | |
3fa581ac | 178 | |
179 | clearmoduledeps(); | |
2c5db955 CP |
180 | |
181 | moddir=getcopyconfigitem("core","moduledir",".",100); | |
182 | modsuffix=getcopyconfigitem("core","modulesuffix",".so",5); | |
183 | ||
3fa581ac | 184 | initmoduledeps(); |
185 | ||
2c5db955 CP |
186 | /* Check for auto-load modules */ |
187 | autoloads=getconfigitems("core","loadmodule"); | |
188 | if (autoloads!=NULL) { | |
189 | mods=(sstring **)(autoloads->content); | |
190 | for (i=0;i<autoloads->cursi;i++) { | |
191 | insmod(mods[i]->content); | |
192 | } | |
193 | } | |
194 | } | |
195 | ||
196 | void initmodules() { | |
197 | array_init(&modules,sizeof(module)); | |
198 | array_setlim1(&modules,5); | |
199 | array_setlim2(&modules,10); | |
200 | ||
201 | moddir=NULL; | |
202 | modsuffix=NULL; | |
203 | modulerehash(); | |
204 | } | |
205 | ||
206 | int insmod(char *modulename) { | |
3f1c5f43 | 207 | int i, n; |
2c5db955 | 208 | module *mods; |
73292a32 | 209 | char buf[1024]; |
87698d77 | 210 | const char *(*verinfo)(void); |
3fa581ac | 211 | struct module_dep *mdp; |
2c5db955 CP |
212 | |
213 | delchars(modulename,"./\\;"); | |
214 | ||
215 | if (isloaded(modulename)) { | |
216 | Error("core",ERR_DEBUG,"Tried to load already loaded module: %s",modulename); | |
217 | return 1; | |
218 | } | |
219 | ||
220 | if (strlen(modulename)>100) { | |
221 | Error("core",ERR_WARNING,"Module name too long: %s",modulename); | |
222 | return 1; | |
223 | } | |
3fa581ac | 224 | |
225 | if ((mdp=getmoduledep(modulename))) { | |
226 | for (i=0;i<mdp->numparents;i++) { | |
227 | if (!isloaded(mdp->parents[i]->name->content)) { | |
228 | if (insmod(mdp->parents[i]->name->content)) { | |
229 | Error("core",ERR_WARNING,"Error loading dependant module %s (needed by %s)", | |
230 | mdp->parents[i]->name->content,modulename); | |
231 | return 1; | |
232 | } | |
233 | } | |
234 | } | |
235 | } else { | |
236 | Error("core",ERR_WARNING,"Loading module %s without dependency information.",modulename); | |
237 | } | |
238 | ||
2c5db955 CP |
239 | i=array_getfreeslot(&modules); |
240 | mods=(module *)(modules.content); | |
3fa581ac | 241 | |
2c5db955 | 242 | sprintf(buf,"%s/%s%s",moddir->content,modulename,modsuffix->content); |
73292a32 | 243 | |
2c5db955 CP |
244 | mods[i].handle=dlopen(buf,RTLD_NOW|RTLD_GLOBAL); |
245 | ||
246 | if(mods[i].handle==NULL) { | |
247 | Error("core",ERR_ERROR,"Loading module %s failed: %s",modulename,dlerror()); | |
248 | array_delslot(&modules,i); | |
249 | return -1; | |
250 | } | |
3fa581ac | 251 | |
2c5db955 CP |
252 | mods[i].name=getsstring(modulename,MODULENAMELEN); |
253 | ||
87698d77 CP |
254 | verinfo=dlsym(mods[i].handle,"_version"); |
255 | if(verinfo) { | |
256 | mods[i].version=verinfo(); | |
257 | } else { | |
258 | mods[i].version=NULL; | |
259 | } | |
260 | ||
2c5db955 CP |
261 | Error("core",ERR_INFO,"Loaded module %s OK.",modulename); |
262 | ||
263 | return 0; | |
264 | } | |
265 | ||
266 | int getindex(char *modulename) { | |
267 | int i; | |
268 | module *mods; | |
269 | ||
270 | mods=(module *)(modules.content); | |
271 | for(i=0;i<modules.cursi;i++) | |
272 | if (!strcmp(mods[i].name->content,modulename)) | |
273 | return i; | |
274 | ||
275 | return -1; | |
276 | } | |
277 | ||
1fab6211 | 278 | char *lsmod(int index) { |
1fab6211 | 279 | module *mods; |
280 | ||
281 | if (index < 0 || index >= modules.cursi) | |
282 | return NULL; | |
283 | ||
284 | mods=(module *)(modules.content); | |
285 | return mods[index].name->content; | |
286 | } | |
287 | ||
87698d77 CP |
288 | const char *lsmodver(int index) { |
289 | module *mods; | |
290 | ||
291 | if (index < 0 || index >= modules.cursi) | |
292 | return NULL; | |
293 | ||
294 | mods=(module *)(modules.content); | |
295 | return mods[index].version; | |
296 | } | |
297 | ||
2c5db955 CP |
298 | int isloaded(char *modulename) { |
299 | if (getindex(modulename)==-1) | |
300 | return 0; | |
301 | else | |
302 | return 1; | |
303 | } | |
304 | ||
3fa581ac | 305 | |
2c5db955 | 306 | int rmmod(char *modulename) { |
3fa581ac | 307 | int i,j; |
2c5db955 | 308 | module *mods; |
3fa581ac | 309 | struct module_dep *mdp; |
2c5db955 CP |
310 | |
311 | delchars(modulename,"./\\;"); | |
312 | ||
313 | i=getindex(modulename); | |
314 | if (i<0) | |
315 | return 1; | |
3fa581ac | 316 | |
317 | if ((mdp=getmoduledep(modulename))) { | |
318 | for (j=0;j<mdp->numchildren;j++) { | |
319 | if (isloaded(mdp->children[j]->name->content)) { | |
320 | if (rmmod(mdp->children[j]->name->content)) { | |
321 | Error("core",ERR_WARNING,"Unable to remove child module %s (depends on %s)", | |
322 | mdp->children[j]->name->content, modulename); | |
323 | return 1; | |
324 | } | |
325 | } | |
326 | } | |
327 | ||
328 | /* We may have removed other modules - reaquire the index number in case it has changed. */ | |
329 | i=getindex(modulename); | |
330 | if (i<0) | |
331 | return 1; | |
332 | } else { | |
333 | Error("core",ERR_WARNING,"Removing module %s without dependency information",modulename); | |
334 | } | |
2c5db955 CP |
335 | |
336 | mods=(module *)(modules.content); | |
337 | ||
c5f1f827 CP |
338 | #ifdef BROKEN_DLCLOSE |
339 | { | |
340 | void (*fini)(); | |
341 | fini = dlsym(mods[i].handle, "__fini"); | |
342 | if(!dlerror()) | |
343 | fini(); | |
344 | } | |
345 | #endif | |
346 | ||
2c5db955 CP |
347 | dlclose(mods[i].handle); |
348 | freesstring(mods[i].name); | |
349 | array_delslot(&modules,i); | |
350 | ||
351 | Error("core",ERR_INFO,"Removed module %s.",modulename); | |
352 | ||
353 | return 0; | |
354 | } | |
3fa581ac | 355 | |
356 | /* Set the reload mark on the indicated module, if loaded, and all its | |
357 | * children */ | |
358 | void setreloadmark(struct module_dep *mdp) { | |
359 | unsigned int i; | |
360 | ||
361 | if (!isloaded(mdp->name->content)) | |
362 | return; | |
363 | ||
364 | for (i=0;i<mdp->numchildren;i++) | |
365 | setreloadmark(mdp->children[i]); | |
366 | ||
367 | mdp->reloading=1; | |
368 | } | |
369 | ||
370 | /* preparereload: this function marks in the dependency tree all the | |
371 | * modules which will be unloaded if this one is removed. */ | |
372 | void preparereload(char *modulename) { | |
373 | unsigned int i; | |
374 | struct module_dep *mdp; | |
375 | ||
376 | delchars(modulename,"./\\;"); | |
377 | ||
378 | /* First, clear the mark off all dependant modules */ | |
379 | for (i=0;i<knownmodules;i++) | |
380 | moduledeps[i].reloading=0; | |
381 | ||
382 | /* Do nothing if this module is not loaded */ | |
383 | i=getindex(modulename); | |
384 | if (i<0) | |
385 | return; | |
386 | ||
387 | if ((mdp=getmoduledep(modulename))) { | |
388 | setreloadmark(mdp); | |
389 | } | |
390 | } | |
391 | ||
392 | /* reloadmarked: this function loads all the modules marked for reloading. | |
393 | * The way the array is ordered means we will do this in the "right" order. | |
394 | * This means we won't do an isloaded() check for each one - we shouldn't | |
395 | * get any "already loaded" warnings so let them come out if they happen. | |
396 | */ | |
397 | void reloadmarked(void) { | |
398 | unsigned int i; | |
399 | ||
400 | for (i=0;i<knownmodules;i++) { | |
401 | if (moduledeps[i].reloading) { | |
402 | insmod(moduledeps[i].name->content); | |
403 | } | |
404 | } | |
405 | } |