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