]>
jfr.im git - irc/quakenet/newserv.git/blob - core/modules.c
4 * Provides functions for dealing with dynamic modules.
10 #include "../lib/array.h"
11 #include "../lib/sstring.h"
12 #include "../lib/irc_string.h"
13 #include "../lib/splitline.h"
14 #include "../lib/strlfunc.h"
15 #include "../lib/valgrind.h"
24 #define DEPFILE "modules.dep"
26 #define MAXMODULES 200
28 /* Module dependency stuff.
30 * "parents" are modules we rely on in order to work.
31 * Whenever we load a module we must ensure all parents are present first.
33 * "children" are modules which rely on us.
34 * Whenever we remove a module, we must ensure all children are removed.
35 * If it's a reload, we will reload them afterwards.
37 * Now, depmod.pl cunningly arranges the file so that each modules' parents are
38 * always defined earlier in the file. So we can allocate and fill in the parents
39 * array as we read the file. Note that we can also arrange for numchildren to be
40 * correct on each node when we've finished.
42 * We then do a second pass. At each node, we allocate the space for the
43 * children array and reset numchildren to 0. We can also then populate our
44 * parents' children array with ourselves, using "numchildren" to get the
47 * At the end of the second pass we should have a correctly filled in array!
50 sstring
*safereload_str
;
51 schedule
*safereload_sched
;
55 unsigned int numparents
;
56 unsigned int numchildren
;
57 struct module_dep
**parents
;
58 struct module_dep
**children
;
59 unsigned int reloading
;
64 struct module_dep moduledeps
[MAXMODULES
];
65 unsigned int knownmodules
=0;
70 /* Clear out and free the dependencies. */
71 void clearmoduledeps() {
74 for (i
=0;i
<knownmodules
;i
++) {
75 freesstring(moduledeps
[i
].name
);
76 if (moduledeps
[i
].parents
) {
77 free(moduledeps
[i
].parents
);
78 moduledeps
[i
].parents
=NULL
;
80 if (moduledeps
[i
].children
) {
81 free(moduledeps
[i
].children
);
82 moduledeps
[i
].children
=NULL
;
89 /* Get a pointer to the given module's dependency record. */
90 struct module_dep
*getmoduledep(const char *name
) {
93 for (i
=0;i
<knownmodules
;i
++) {
94 if (!strcmp(name
,moduledeps
[i
].name
->content
))
95 return &(moduledeps
[i
]);
101 /* Populate the dependency array. Read the monster description above for details.
102 * This relies on moddir being set, so call this after setting it up. */
103 void initmoduledeps() {
109 struct module_dep
*mdp
, *tmdp
;
112 sprintf(buf
,"%s/%s",moddir
->content
,DEPFILE
);
114 if (!(fp
=fopen(buf
,"r"))) {
115 Error("core",ERR_WARNING
,"Unable to open module dependency file: %s",buf
);
119 fgets(buf
, sizeof(buf
), fp
);
123 /* Chomp off that ruddy newline. */
124 for (ch
=buf
;*ch
;ch
++) {
125 if (*ch
=='\n' || *ch
=='\r') {
131 /* We have a space-delimited list of things. Whatever will we do with that? :) */
132 largc
=splitline(buf
,largv
,100,0);
137 /* Add us to the array */
140 Error("core",ERR_ERROR
,
141 "Too many modules in dependency file; rebuild with higher MAXMODULES. Module dependencies disabled.\n");
147 mdp
=&(moduledeps
[i
]);
149 mdp
->name
=getsstring(largv
[0],100);
150 mdp
->numparents
=largc
-1;
151 mdp
->numchildren
=0; /* peace, for now */
152 mdp
->parents
=malloc(mdp
->numparents
* sizeof(struct module_dep
*));
154 /* Fill in the parents array */
155 for (i
=0;i
<(largc
-1);i
++) {
156 if (!(mdp
->parents
[i
]=getmoduledep(largv
[i
+1]))) {
157 Error("core",ERR_WARNING
,"Couldn't find parent module %s of %s. Module dependencies disabled.",
158 largv
[i
+1],largv
[0]);
163 mdp
->parents
[i
]->numchildren
++; /* break the bad news */
170 for (i
=0;i
<knownmodules
;i
++) {
171 mdp
=&(moduledeps
[i
]);
173 /* Allocate child array */
174 if (mdp
->numchildren
) {
175 mdp
->children
=malloc(mdp
->numchildren
* sizeof(struct module_dep
*));
176 mdp
->numchildren
=0; /* if only real life were this simple */
179 /* Now fill in the children arrays of our parents (bear with me on this) */
180 for (j
=0;j
<mdp
->numparents
;j
++) {
181 tmdp
=mdp
->parents
[j
]; /* This is just... */
183 tmdp
->children
[tmdp
->numchildren
++]=mdp
; /* ... to give this line a chance at making sense */
189 void modulerehash() {
198 freesstring(modsuffix
);
202 moddir
=getcopyconfigitem("core","moduledir",".",100);
203 modsuffix
=getcopyconfigitem("core","modulesuffix",".so",5);
207 /* Check for auto-load modules */
208 autoloads
=getconfigitems("core","loadmodule");
209 if (autoloads
!=NULL
) {
210 mods
=(sstring
**)(autoloads
->content
);
211 for (i
=0;i
<autoloads
->cursi
;i
++) {
212 insmod(mods
[i
]->content
);
218 array_init(&modules
,sizeof(module));
219 array_setlim1(&modules
,5);
220 array_setlim2(&modules
,10);
227 int insmod(char *modulename
) {
230 char buf
[1024], modulebuf
[1024];
231 const char *(*verinfo
)(const char **);
232 struct module_dep
*mdp
;
234 strlcpy(modulebuf
, modulename
, sizeof(modulebuf
));
235 delchars(modulebuf
,"./\\;");
237 if (isloaded(modulebuf
)) {
238 Error("core",ERR_DEBUG
,"Tried to load already loaded module: %s",modulebuf
);
242 if (strlen(modulebuf
)>100) {
243 Error("core",ERR_WARNING
,"Module name too long: %s",modulebuf
);
247 if ((mdp
=getmoduledep(modulebuf
))) {
248 for (i
=0;i
<mdp
->numparents
;i
++) {
249 if (!isloaded(mdp
->parents
[i
]->name
->content
)) {
250 if (insmod(mdp
->parents
[i
]->name
->content
)) {
251 Error("core",ERR_WARNING
,"Error loading dependant module %s (needed by %s)",
252 mdp
->parents
[i
]->name
->content
,modulebuf
);
258 Error("core",ERR_WARNING
,"Loading module %s without dependency information.",modulebuf
);
261 i
=array_getfreeslot(&modules
);
262 mods
=(module *)(modules
.content
);
264 sprintf(buf
,"%s/%s%s",moddir
->content
,modulebuf
,modsuffix
->content
);
266 mods
[i
].handle
=dlopen(buf
,RTLD_NOW
|RTLD_GLOBAL
);
268 if(mods
[i
].handle
==NULL
) {
269 Error("core",ERR_ERROR
,"Loading module %s failed: %s",modulebuf
,dlerror());
270 array_delslot(&modules
,i
);
274 mods
[i
].name
=getsstring(modulebuf
,MODULENAMELEN
);
276 verinfo
=dlsym(mods
[i
].handle
,"_version");
278 mods
[i
].buildid
=verinfo(&mods
[i
].version
);
280 mods
[i
].version
=NULL
;
281 mods
[i
].buildid
=NULL
;
284 mods
[i
].loadedsince
= time(NULL
);
285 Error("core",ERR_INFO
,"Loaded module %s OK.",modulebuf
);
290 int getindex(char *modulename
) {
294 mods
=(module *)(modules
.content
);
295 for(i
=0;i
<modules
.cursi
;i
++)
296 if (!strcmp(mods
[i
].name
->content
,modulename
))
302 char *lsmod(int index
, const char **ver
, const char **buildid
, time_t *t
) {
305 if (index
< 0 || index
>= modules
.cursi
)
308 mods
=(module *)(modules
.content
);
310 *ver
=mods
[index
].version
;
312 *buildid
=mods
[index
].buildid
;
314 *t
=mods
[index
].loadedsince
;
316 return mods
[index
].name
->content
;
319 int isloaded(char *modulename
) {
320 if (getindex(modulename
)==-1)
327 int rmmod(char *modulename
, int close
) {
330 struct module_dep
*mdp
;
331 char modulebuf
[1024];
333 strlcpy(modulebuf
, modulename
, sizeof(modulebuf
));
334 delchars(modulebuf
,"./\\;");
336 i
=getindex(modulebuf
);
340 if ((mdp
=getmoduledep(modulebuf
))) {
341 for (j
=0;j
<mdp
->numchildren
;j
++) {
342 if (isloaded(mdp
->children
[j
]->name
->content
)) {
343 if (rmmod(mdp
->children
[j
]->name
->content
, close
)) {
344 Error("core",ERR_WARNING
,"Unable to remove child module %s (depends on %s)",
345 mdp
->children
[j
]->name
->content
, modulebuf
);
351 /* We may have removed other modules - reaquire the index number in case it has changed. */
352 i
=getindex(modulebuf
);
356 Error("core",ERR_WARNING
,"Removing module %s without dependency information",modulebuf
);
359 mods
=(module *)(modules
.content
);
362 #ifdef BROKEN_DLCLOSE
367 fini
= dlsym(mods
[i
].handle
, "__fini");
371 dlclose(mods
[i
].handle
);
373 freesstring(mods
[i
].name
);
374 array_delslot(&modules
,i
);
376 Error("core",ERR_INFO
,"Removed module %s.",modulebuf
);
381 /* Set the reload mark on the indicated module, if loaded, and all its
383 void setreloadmark(struct module_dep
*mdp
) {
386 if (!isloaded(mdp
->name
->content
))
389 for (i
=0;i
<mdp
->numchildren
;i
++)
390 setreloadmark(mdp
->children
[i
]);
395 /* preparereload: this function marks in the dependency tree all the
396 * modules which will be unloaded if this one is removed. */
397 void preparereload(char *modulename
) {
399 struct module_dep
*mdp
;
400 char modulebuf
[1024];
402 strlcpy(modulebuf
, modulename
, sizeof(modulebuf
));
403 delchars(modulebuf
,"./\\;");
405 /* First, clear the mark off all dependant modules */
406 for (i
=0;i
<knownmodules
;i
++)
407 moduledeps
[i
].reloading
=0;
409 /* Do nothing if this module is not loaded */
410 if (getindex(modulebuf
)<0)
413 if ((mdp
=getmoduledep(modulebuf
))) {
418 /* reloadmarked: this function loads all the modules marked for reloading.
419 * The way the array is ordered means we will do this in the "right" order.
420 * This means we won't do an isloaded() check for each one - we shouldn't
421 * get any "already loaded" warnings so let them come out if they happen.
423 void reloadmarked(void) {
426 for (i
=0;i
<knownmodules
;i
++) {
427 if (moduledeps
[i
].reloading
) {
428 insmod(moduledeps
[i
].name
->content
);
433 void safereloadcallback(void *arg
) {
434 safereload_sched
=NULL
;
439 preparereload(safereload_str
->content
);
440 rmmod(safereload_str
->content
, 1);
441 insmod(safereload_str
->content
);
444 freesstring(safereload_str
);
448 void safereload(char *themodule
) {
450 freesstring(safereload_str
);
452 safereload_str
=getsstring(themodule
, 100);
454 if (safereload_sched
)
455 deleteschedule(safereload_sched
, safereloadcallback
, NULL
);
457 scheduleoneshot(1, safereloadcallback
, NULL
);
460 void newserv_shutdown() {
464 while (modules
.cursi
) {
465 mods
=(module *)(modules
.content
);
467 strlcpy(buf
, mods
[0].name
->content
, sizeof(buf
));
469 /* Unload the module unless we're running on Valgrind -
470 * in which case unloading the module would invalidate
471 * stacktraces Valgrind has captured so far. */
472 rmmod(buf
, !RUNNING_ON_VALGRIND
);
481 freesstring(modsuffix
);
483 Error("core",ERR_INFO
,"All modules removed. Exiting.");
486 /* very slow, make sure you cache the pointer! */
487 void *ndlsym(char *modulename
, char *fn
) {
488 module *mods
=(module *)(modules
.content
);
489 int i
=getindex(modulename
);
494 return dlsym(mods
[i
].handle
, fn
);