]> jfr.im git - irc/quakenet/newserv.git/blame - core/modules.c
Don't unload modules at shutdown when we're running on Valgrind.
[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"
707c5824 14#include "../lib/strlfunc.h"
95ab9d6b 15#include "../lib/valgrind.h"
2c5db955
CP
16#include "config.h"
17#include "error.h"
18#include <stdio.h>
19#include <string.h>
3f1c5f43
CP
20#include <errno.h>
21#include <unistd.h>
65f2c6a3 22#include "schedule.h"
2c5db955 23
3fa581ac 24#define DEPFILE "modules.dep"
25
5605177a 26#define MAXMODULES 200
3fa581ac 27
28/* Module dependency stuff.
29 *
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.
32 *
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.
36 *
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.
41 *
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
45 * list in order.
46 *
47 * At the end of the second pass we should have a correctly filled in array!
48 */
49
65f2c6a3 50sstring *safereload_str;
51schedule *safereload_sched;
52
3fa581ac 53struct module_dep {
54 sstring *name;
55 unsigned int numparents;
56 unsigned int numchildren;
57 struct module_dep **parents;
58 struct module_dep **children;
59 unsigned int reloading;
60};
61
2c5db955
CP
62array modules;
63
3fa581ac 64struct module_dep moduledeps[MAXMODULES];
65unsigned int knownmodules=0;
66
2c5db955
CP
67sstring *moddir;
68sstring *modsuffix;
69
3fa581ac 70/* Clear out and free the dependencies. */
71void clearmoduledeps() {
72 unsigned int i;
73
74 for (i=0;i<knownmodules;i++) {
707c5824 75 freesstring(moduledeps[i].name);
b46e727d 76 if (moduledeps[i].parents) {
77 free(moduledeps[i].parents);
78 moduledeps[i].parents=NULL;
79 }
80 if (moduledeps[i].children) {
81 free(moduledeps[i].children);
82 moduledeps[i].children=NULL;
83 }
3fa581ac 84 }
85
86 knownmodules=0;
87}
88
89/* Get a pointer to the given module's dependency record. */
90struct module_dep *getmoduledep(const char *name) {
91 unsigned int i;
92
93 for (i=0;i<knownmodules;i++) {
94 if (!strcmp(name,moduledeps[i].name->content))
95 return &(moduledeps[i]);
96 }
97
98 return NULL;
99}
100
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. */
103void initmoduledeps() {
104 FILE *fp;
105 char buf[1024];
106 char *largv[100];
107 char largc;
108 char *ch;
b46e727d 109 struct module_dep *mdp, *tmdp;
3fa581ac 110 unsigned int i,j;
111
112 sprintf(buf,"%s/%s",moddir->content,DEPFILE);
113
114 if (!(fp=fopen(buf,"r"))) {
115 Error("core",ERR_WARNING,"Unable to open module dependency file: %s",buf);
116 } else {
117 /* First pass */
118 while (!feof(fp)) {
119 fgets(buf, sizeof(buf), fp);
120 if (feof(fp))
121 break;
122
123 /* Chomp off that ruddy newline. */
124 for (ch=buf;*ch;ch++) {
125 if (*ch=='\n' || *ch=='\r') {
126 *ch='\0';
127 break;
128 }
129 }
130
131 /* We have a space-delimited list of things. Whatever will we do with that? :) */
132 largc=splitline(buf,largv,100,0);
133
134 if (largc<1)
135 continue;
136
137 /* Add us to the array */
138 i=knownmodules++;
139 if (i>=MAXMODULES) {
b46e727d 140 Error("core",ERR_ERROR,
141 "Too many modules in dependency file; rebuild with higher MAXMODULES. Module dependencies disabled.\n");
142 clearmoduledeps();
2fd779e3 143 fclose(fp);
3fa581ac 144 return;
145 }
146
147 mdp=&(moduledeps[i]);
148
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 *));
153
154 /* Fill in the parents array */
155 for (i=0;i<(largc-1);i++) {
156 if (!(mdp->parents[i]=getmoduledep(largv[i+1]))) {
b46e727d 157 Error("core",ERR_WARNING,"Couldn't find parent module %s of %s. Module dependencies disabled.",
158 largv[i+1],largv[0]);
159 clearmoduledeps();
2fd779e3 160 fclose(fp);
b46e727d 161 return;
3fa581ac 162 }
163 mdp->parents[i]->numchildren++; /* break the bad news */
164 }
165 }
2fd779e3
GB
166
167 fclose(fp);
3fa581ac 168
169 /* Second pass */
170 for (i=0;i<knownmodules;i++) {
171 mdp=&(moduledeps[i]);
172
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 */
177 }
178
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... */
182
183 tmdp->children[tmdp->numchildren++]=mdp; /* ... to give this line a chance at making sense */
184 }
185 }
186 }
187}
188
2c5db955
CP
189void modulerehash() {
190 int i;
191 sstring **mods;
192 array *autoloads;
193
194 if (moddir!=NULL)
195 freesstring(moddir);
196
197 if (modsuffix!=NULL)
198 freesstring(modsuffix);
3fa581ac 199
200 clearmoduledeps();
2c5db955
CP
201
202 moddir=getcopyconfigitem("core","moduledir",".",100);
203 modsuffix=getcopyconfigitem("core","modulesuffix",".so",5);
204
3fa581ac 205 initmoduledeps();
206
2c5db955
CP
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);
213 }
214 }
215}
216
217void initmodules() {
218 array_init(&modules,sizeof(module));
219 array_setlim1(&modules,5);
220 array_setlim2(&modules,10);
221
222 moddir=NULL;
223 modsuffix=NULL;
224 modulerehash();
225}
226
227int insmod(char *modulename) {
b46e727d 228 int i;
2c5db955 229 module *mods;
b2e24d2f 230 char buf[1024], modulebuf[1024];
2fb74c5e 231 const char *(*verinfo)(const char **);
3fa581ac 232 struct module_dep *mdp;
2c5db955 233
b2e24d2f
CP
234 strlcpy(modulebuf, modulename, sizeof(modulebuf));
235 delchars(modulebuf,"./\\;");
2c5db955 236
b2e24d2f
CP
237 if (isloaded(modulebuf)) {
238 Error("core",ERR_DEBUG,"Tried to load already loaded module: %s",modulebuf);
2c5db955
CP
239 return 1;
240 }
241
b2e24d2f
CP
242 if (strlen(modulebuf)>100) {
243 Error("core",ERR_WARNING,"Module name too long: %s",modulebuf);
2c5db955
CP
244 return 1;
245 }
3fa581ac 246
b2e24d2f 247 if ((mdp=getmoduledep(modulebuf))) {
3fa581ac 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)",
b2e24d2f 252 mdp->parents[i]->name->content,modulebuf);
3fa581ac 253 return 1;
254 }
255 }
256 }
257 } else {
b2e24d2f 258 Error("core",ERR_WARNING,"Loading module %s without dependency information.",modulebuf);
3fa581ac 259 }
260
2c5db955
CP
261 i=array_getfreeslot(&modules);
262 mods=(module *)(modules.content);
3fa581ac 263
b2e24d2f 264 sprintf(buf,"%s/%s%s",moddir->content,modulebuf,modsuffix->content);
73292a32 265
2c5db955
CP
266 mods[i].handle=dlopen(buf,RTLD_NOW|RTLD_GLOBAL);
267
268 if(mods[i].handle==NULL) {
b2e24d2f 269 Error("core",ERR_ERROR,"Loading module %s failed: %s",modulebuf,dlerror());
2c5db955
CP
270 array_delslot(&modules,i);
271 return -1;
272 }
3fa581ac 273
b2e24d2f 274 mods[i].name=getsstring(modulebuf,MODULENAMELEN);
2c5db955 275
87698d77
CP
276 verinfo=dlsym(mods[i].handle,"_version");
277 if(verinfo) {
2fb74c5e
CP
278 mods[i].buildid=verinfo(&mods[i].version);
279 } else {
87698d77 280 mods[i].version=NULL;
2fb74c5e 281 mods[i].buildid=NULL;
87698d77
CP
282 }
283
2fb74c5e 284 mods[i].loadedsince = time(NULL);
b2e24d2f 285 Error("core",ERR_INFO,"Loaded module %s OK.",modulebuf);
2c5db955
CP
286
287 return 0;
288}
289
290int getindex(char *modulename) {
291 int i;
292 module *mods;
293
294 mods=(module *)(modules.content);
295 for(i=0;i<modules.cursi;i++)
296 if (!strcmp(mods[i].name->content,modulename))
297 return i;
298
299 return -1;
300}
301
2fb74c5e 302char *lsmod(int index, const char **ver, const char **buildid, time_t *t) {
1fab6211 303 module *mods;
304
305 if (index < 0 || index >= modules.cursi)
306 return NULL;
307
308 mods=(module *)(modules.content);
2fb74c5e
CP
309 if(ver)
310 *ver=mods[index].version;
311 if(buildid)
312 *buildid=mods[index].buildid;
313 if(t)
314 *t=mods[index].loadedsince;
87698d77 315
2fb74c5e 316 return mods[index].name->content;
87698d77
CP
317}
318
2c5db955
CP
319int isloaded(char *modulename) {
320 if (getindex(modulename)==-1)
321 return 0;
322 else
323 return 1;
324}
325
3fa581ac 326
95ab9d6b 327int rmmod(char *modulename, int close) {
3fa581ac 328 int i,j;
2c5db955 329 module *mods;
3fa581ac 330 struct module_dep *mdp;
b2e24d2f
CP
331 char modulebuf[1024];
332
333 strlcpy(modulebuf, modulename, sizeof(modulebuf));
334 delchars(modulebuf,"./\\;");
2c5db955 335
b2e24d2f 336 i=getindex(modulebuf);
2c5db955
CP
337 if (i<0)
338 return 1;
3fa581ac 339
b2e24d2f 340 if ((mdp=getmoduledep(modulebuf))) {
3fa581ac 341 for (j=0;j<mdp->numchildren;j++) {
342 if (isloaded(mdp->children[j]->name->content)) {
95ab9d6b 343 if (rmmod(mdp->children[j]->name->content, close)) {
3fa581ac 344 Error("core",ERR_WARNING,"Unable to remove child module %s (depends on %s)",
b2e24d2f 345 mdp->children[j]->name->content, modulebuf);
3fa581ac 346 return 1;
347 }
348 }
349 }
350
351 /* We may have removed other modules - reaquire the index number in case it has changed. */
b2e24d2f 352 i=getindex(modulebuf);
3fa581ac 353 if (i<0)
354 return 1;
355 } else {
b2e24d2f 356 Error("core",ERR_WARNING,"Removing module %s without dependency information",modulebuf);
3fa581ac 357 }
2c5db955
CP
358
359 mods=(module *)(modules.content);
360
95ab9d6b 361 if (!close
c5f1f827 362#ifdef BROKEN_DLCLOSE
95ab9d6b
GB
363 || 1
364#endif
365 ) {
c5f1f827
CP
366 void (*fini)();
367 fini = dlsym(mods[i].handle, "__fini");
368 if(!dlerror())
369 fini();
95ab9d6b
GB
370 } else
371 dlclose(mods[i].handle);
c5f1f827 372
2c5db955
CP
373 freesstring(mods[i].name);
374 array_delslot(&modules,i);
375
b2e24d2f 376 Error("core",ERR_INFO,"Removed module %s.",modulebuf);
2c5db955
CP
377
378 return 0;
379}
3fa581ac 380
381/* Set the reload mark on the indicated module, if loaded, and all its
382 * children */
383void setreloadmark(struct module_dep *mdp) {
384 unsigned int i;
385
386 if (!isloaded(mdp->name->content))
387 return;
388
389 for (i=0;i<mdp->numchildren;i++)
390 setreloadmark(mdp->children[i]);
391
392 mdp->reloading=1;
393}
394
395/* preparereload: this function marks in the dependency tree all the
396 * modules which will be unloaded if this one is removed. */
397void preparereload(char *modulename) {
398 unsigned int i;
399 struct module_dep *mdp;
2b3fcd0e
CP
400 char modulebuf[1024];
401
402 strlcpy(modulebuf, modulename, sizeof(modulebuf));
403 delchars(modulebuf,"./\\;");
3fa581ac 404
405 /* First, clear the mark off all dependant modules */
406 for (i=0;i<knownmodules;i++)
407 moduledeps[i].reloading=0;
408
409 /* Do nothing if this module is not loaded */
0555113a 410 if (getindex(modulebuf)<0)
3fa581ac 411 return;
412
2b3fcd0e 413 if ((mdp=getmoduledep(modulebuf))) {
3fa581ac 414 setreloadmark(mdp);
415 }
416}
417
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.
422 */
423void reloadmarked(void) {
424 unsigned int i;
425
426 for (i=0;i<knownmodules;i++) {
427 if (moduledeps[i].reloading) {
428 insmod(moduledeps[i].name->content);
429 }
430 }
431}
65f2c6a3 432
433void safereloadcallback(void *arg) {
434 safereload_sched=NULL;
435
436 if (!safereload_str)
437 return;
438
439 preparereload(safereload_str->content);
95ab9d6b 440 rmmod(safereload_str->content, 1);
65f2c6a3 441 insmod(safereload_str->content);
442 reloadmarked();
443
444 freesstring(safereload_str);
445 safereload_str=NULL;
446}
447
448void safereload(char *themodule) {
449 if (safereload_str)
450 freesstring(safereload_str);
451
452 safereload_str=getsstring(themodule, 100);
453
454 if (safereload_sched)
455 deleteschedule(safereload_sched, safereloadcallback, NULL);
456
457 scheduleoneshot(1, safereloadcallback, NULL);
458}
83951d54 459
460void newserv_shutdown() {
461 module *mods;
707c5824
CP
462 char buf[1024];
463
83951d54 464 while (modules.cursi) {
465 mods=(module *)(modules.content);
707c5824
CP
466
467 strlcpy(buf, mods[0].name->content, sizeof(buf));
95ab9d6b
GB
468
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);
83951d54 473 }
474
707c5824
CP
475 clearmoduledeps();
476
477 if (moddir!=NULL)
478 freesstring(moddir);
479
480 if (modsuffix!=NULL)
481 freesstring(modsuffix);
482
83951d54 483 Error("core",ERR_INFO,"All modules removed. Exiting.");
484}
4e4920a4
CP
485
486/* very slow, make sure you cache the pointer! */
487void *ndlsym(char *modulename, char *fn) {
488 module *mods=(module *)(modules.content);
489 int i=getindex(modulename);
490
491 if (i<0)
492 return NULL;
493
494 return dlsym(mods[i].handle, fn);
495}