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