]> jfr.im git - irc/quakenet/newserv.git/blame - core/modules.c
Added support for +T and +M modes (mode tracking only - we don't actually do
[irc/quakenet/newserv.git] / core / modules.c
CommitLineData
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
47struct 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
56array modules;
57
3fa581ac 58struct module_dep moduledeps[MAXMODULES];
59unsigned int knownmodules=0;
60
2c5db955
CP
61sstring *moddir;
62sstring *modsuffix;
63
3fa581ac 64/* Clear out and free the dependencies. */
65void 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. */
77struct 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. */
90void 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
168void 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
196void 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
206int 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
266int 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 278char *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
288const 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
298int isloaded(char *modulename) {
299 if (getindex(modulename)==-1)
300 return 0;
301 else
302 return 1;
303}
304
3fa581ac 305
2c5db955 306int 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 */
358void 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. */
372void 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 */
397void 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}