]> jfr.im git - irc/quakenet/newserv.git/blame - core/modules.c
Fix a few strings that are altered illegally.
[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"
2c5db955
CP
15#include "config.h"
16#include "error.h"
17#include <stdio.h>
18#include <string.h>
3f1c5f43
CP
19#include <errno.h>
20#include <unistd.h>
65f2c6a3 21#include "schedule.h"
2c5db955 22
3fa581ac 23#define DEPFILE "modules.dep"
24
25#define MAXMODULES 100
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
65f2c6a3 49sstring *safereload_str;
50schedule *safereload_sched;
51
3fa581ac 52struct 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
2c5db955
CP
61array modules;
62
3fa581ac 63struct module_dep moduledeps[MAXMODULES];
64unsigned int knownmodules=0;
65
2c5db955
CP
66sstring *moddir;
67sstring *modsuffix;
68
3fa581ac 69/* Clear out and free the dependencies. */
70void clearmoduledeps() {
71 unsigned int i;
72
73 for (i=0;i<knownmodules;i++) {
707c5824 74 freesstring(moduledeps[i].name);
b46e727d 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 }
3fa581ac 83 }
84
85 knownmodules=0;
86}
87
88/* Get a pointer to the given module's dependency record. */
89struct 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. */
102void initmoduledeps() {
103 FILE *fp;
104 char buf[1024];
105 char *largv[100];
106 char largc;
107 char *ch;
b46e727d 108 struct module_dep *mdp, *tmdp;
3fa581ac 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) {
b46e727d 139 Error("core",ERR_ERROR,
140 "Too many modules in dependency file; rebuild with higher MAXMODULES. Module dependencies disabled.\n");
141 clearmoduledeps();
3fa581ac 142 return;
143 }
144
145 mdp=&(moduledeps[i]);
146
147 mdp->name=getsstring(largv[0],100);
148 mdp->numparents=largc-1;
149 mdp->numchildren=0; /* peace, for now */
150 mdp->parents=malloc(mdp->numparents * sizeof(struct module_dep *));
151
152 /* Fill in the parents array */
153 for (i=0;i<(largc-1);i++) {
154 if (!(mdp->parents[i]=getmoduledep(largv[i+1]))) {
b46e727d 155 Error("core",ERR_WARNING,"Couldn't find parent module %s of %s. Module dependencies disabled.",
156 largv[i+1],largv[0]);
157 clearmoduledeps();
158 return;
3fa581ac 159 }
160 mdp->parents[i]->numchildren++; /* break the bad news */
161 }
162 }
163
164 /* Second pass */
165 for (i=0;i<knownmodules;i++) {
166 mdp=&(moduledeps[i]);
167
168 /* Allocate child array */
169 if (mdp->numchildren) {
170 mdp->children=malloc(mdp->numchildren * sizeof(struct module_dep *));
171 mdp->numchildren=0; /* if only real life were this simple */
172 }
173
174 /* Now fill in the children arrays of our parents (bear with me on this) */
175 for (j=0;j<mdp->numparents;j++) {
176 tmdp=mdp->parents[j]; /* This is just... */
177
178 tmdp->children[tmdp->numchildren++]=mdp; /* ... to give this line a chance at making sense */
179 }
180 }
181 }
182}
183
2c5db955
CP
184void modulerehash() {
185 int i;
186 sstring **mods;
187 array *autoloads;
188
189 if (moddir!=NULL)
190 freesstring(moddir);
191
192 if (modsuffix!=NULL)
193 freesstring(modsuffix);
3fa581ac 194
195 clearmoduledeps();
2c5db955
CP
196
197 moddir=getcopyconfigitem("core","moduledir",".",100);
198 modsuffix=getcopyconfigitem("core","modulesuffix",".so",5);
199
3fa581ac 200 initmoduledeps();
201
2c5db955
CP
202 /* Check for auto-load modules */
203 autoloads=getconfigitems("core","loadmodule");
204 if (autoloads!=NULL) {
205 mods=(sstring **)(autoloads->content);
206 for (i=0;i<autoloads->cursi;i++) {
207 insmod(mods[i]->content);
208 }
209 }
210}
211
212void initmodules() {
213 array_init(&modules,sizeof(module));
214 array_setlim1(&modules,5);
215 array_setlim2(&modules,10);
216
217 moddir=NULL;
218 modsuffix=NULL;
219 modulerehash();
220}
221
222int insmod(char *modulename) {
b46e727d 223 int i;
2c5db955 224 module *mods;
b2e24d2f 225 char buf[1024], modulebuf[1024];
2fb74c5e 226 const char *(*verinfo)(const char **);
3fa581ac 227 struct module_dep *mdp;
2c5db955 228
b2e24d2f
CP
229 strlcpy(modulebuf, modulename, sizeof(modulebuf));
230 delchars(modulebuf,"./\\;");
2c5db955 231
b2e24d2f
CP
232 if (isloaded(modulebuf)) {
233 Error("core",ERR_DEBUG,"Tried to load already loaded module: %s",modulebuf);
2c5db955
CP
234 return 1;
235 }
236
b2e24d2f
CP
237 if (strlen(modulebuf)>100) {
238 Error("core",ERR_WARNING,"Module name too long: %s",modulebuf);
2c5db955
CP
239 return 1;
240 }
3fa581ac 241
b2e24d2f 242 if ((mdp=getmoduledep(modulebuf))) {
3fa581ac 243 for (i=0;i<mdp->numparents;i++) {
244 if (!isloaded(mdp->parents[i]->name->content)) {
245 if (insmod(mdp->parents[i]->name->content)) {
246 Error("core",ERR_WARNING,"Error loading dependant module %s (needed by %s)",
b2e24d2f 247 mdp->parents[i]->name->content,modulebuf);
3fa581ac 248 return 1;
249 }
250 }
251 }
252 } else {
b2e24d2f 253 Error("core",ERR_WARNING,"Loading module %s without dependency information.",modulebuf);
3fa581ac 254 }
255
2c5db955
CP
256 i=array_getfreeslot(&modules);
257 mods=(module *)(modules.content);
3fa581ac 258
b2e24d2f 259 sprintf(buf,"%s/%s%s",moddir->content,modulebuf,modsuffix->content);
73292a32 260
2c5db955
CP
261 mods[i].handle=dlopen(buf,RTLD_NOW|RTLD_GLOBAL);
262
263 if(mods[i].handle==NULL) {
b2e24d2f 264 Error("core",ERR_ERROR,"Loading module %s failed: %s",modulebuf,dlerror());
2c5db955
CP
265 array_delslot(&modules,i);
266 return -1;
267 }
3fa581ac 268
b2e24d2f 269 mods[i].name=getsstring(modulebuf,MODULENAMELEN);
2c5db955 270
87698d77
CP
271 verinfo=dlsym(mods[i].handle,"_version");
272 if(verinfo) {
2fb74c5e
CP
273 mods[i].buildid=verinfo(&mods[i].version);
274 } else {
87698d77 275 mods[i].version=NULL;
2fb74c5e 276 mods[i].buildid=NULL;
87698d77
CP
277 }
278
2fb74c5e 279 mods[i].loadedsince = time(NULL);
b2e24d2f 280 Error("core",ERR_INFO,"Loaded module %s OK.",modulebuf);
2c5db955
CP
281
282 return 0;
283}
284
285int getindex(char *modulename) {
286 int i;
287 module *mods;
288
289 mods=(module *)(modules.content);
290 for(i=0;i<modules.cursi;i++)
291 if (!strcmp(mods[i].name->content,modulename))
292 return i;
293
294 return -1;
295}
296
2fb74c5e 297char *lsmod(int index, const char **ver, const char **buildid, time_t *t) {
1fab6211 298 module *mods;
299
300 if (index < 0 || index >= modules.cursi)
301 return NULL;
302
303 mods=(module *)(modules.content);
2fb74c5e
CP
304 if(ver)
305 *ver=mods[index].version;
306 if(buildid)
307 *buildid=mods[index].buildid;
308 if(t)
309 *t=mods[index].loadedsince;
87698d77 310
2fb74c5e 311 return mods[index].name->content;
87698d77
CP
312}
313
2c5db955
CP
314int isloaded(char *modulename) {
315 if (getindex(modulename)==-1)
316 return 0;
317 else
318 return 1;
319}
320
3fa581ac 321
2c5db955 322int rmmod(char *modulename) {
3fa581ac 323 int i,j;
2c5db955 324 module *mods;
3fa581ac 325 struct module_dep *mdp;
b2e24d2f
CP
326 char modulebuf[1024];
327
328 strlcpy(modulebuf, modulename, sizeof(modulebuf));
329 delchars(modulebuf,"./\\;");
2c5db955 330
b2e24d2f 331 i=getindex(modulebuf);
2c5db955
CP
332 if (i<0)
333 return 1;
3fa581ac 334
b2e24d2f 335 if ((mdp=getmoduledep(modulebuf))) {
3fa581ac 336 for (j=0;j<mdp->numchildren;j++) {
337 if (isloaded(mdp->children[j]->name->content)) {
338 if (rmmod(mdp->children[j]->name->content)) {
339 Error("core",ERR_WARNING,"Unable to remove child module %s (depends on %s)",
b2e24d2f 340 mdp->children[j]->name->content, modulebuf);
3fa581ac 341 return 1;
342 }
343 }
344 }
345
346 /* We may have removed other modules - reaquire the index number in case it has changed. */
b2e24d2f 347 i=getindex(modulebuf);
3fa581ac 348 if (i<0)
349 return 1;
350 } else {
b2e24d2f 351 Error("core",ERR_WARNING,"Removing module %s without dependency information",modulebuf);
3fa581ac 352 }
2c5db955
CP
353
354 mods=(module *)(modules.content);
355
c5f1f827
CP
356#ifdef BROKEN_DLCLOSE
357 {
358 void (*fini)();
359 fini = dlsym(mods[i].handle, "__fini");
360 if(!dlerror())
361 fini();
362 }
363#endif
364
2c5db955
CP
365 dlclose(mods[i].handle);
366 freesstring(mods[i].name);
367 array_delslot(&modules,i);
368
b2e24d2f 369 Error("core",ERR_INFO,"Removed module %s.",modulebuf);
2c5db955
CP
370
371 return 0;
372}
3fa581ac 373
374/* Set the reload mark on the indicated module, if loaded, and all its
375 * children */
376void setreloadmark(struct module_dep *mdp) {
377 unsigned int i;
378
379 if (!isloaded(mdp->name->content))
380 return;
381
382 for (i=0;i<mdp->numchildren;i++)
383 setreloadmark(mdp->children[i]);
384
385 mdp->reloading=1;
386}
387
388/* preparereload: this function marks in the dependency tree all the
389 * modules which will be unloaded if this one is removed. */
390void preparereload(char *modulename) {
391 unsigned int i;
392 struct module_dep *mdp;
393
394 delchars(modulename,"./\\;");
395
396 /* First, clear the mark off all dependant modules */
397 for (i=0;i<knownmodules;i++)
398 moduledeps[i].reloading=0;
399
400 /* Do nothing if this module is not loaded */
401 i=getindex(modulename);
402 if (i<0)
403 return;
404
405 if ((mdp=getmoduledep(modulename))) {
406 setreloadmark(mdp);
407 }
408}
409
410/* reloadmarked: this function loads all the modules marked for reloading.
411 * The way the array is ordered means we will do this in the "right" order.
412 * This means we won't do an isloaded() check for each one - we shouldn't
413 * get any "already loaded" warnings so let them come out if they happen.
414 */
415void reloadmarked(void) {
416 unsigned int i;
417
418 for (i=0;i<knownmodules;i++) {
419 if (moduledeps[i].reloading) {
420 insmod(moduledeps[i].name->content);
421 }
422 }
423}
65f2c6a3 424
425void safereloadcallback(void *arg) {
426 safereload_sched=NULL;
427
428 if (!safereload_str)
429 return;
430
431 preparereload(safereload_str->content);
432 rmmod(safereload_str->content);
433 insmod(safereload_str->content);
434 reloadmarked();
435
436 freesstring(safereload_str);
437 safereload_str=NULL;
438}
439
440void safereload(char *themodule) {
441 if (safereload_str)
442 freesstring(safereload_str);
443
444 safereload_str=getsstring(themodule, 100);
445
446 if (safereload_sched)
447 deleteschedule(safereload_sched, safereloadcallback, NULL);
448
449 scheduleoneshot(1, safereloadcallback, NULL);
450}
83951d54 451
452void newserv_shutdown() {
453 module *mods;
707c5824
CP
454 char buf[1024];
455
83951d54 456 while (modules.cursi) {
457 mods=(module *)(modules.content);
707c5824
CP
458
459 strlcpy(buf, mods[0].name->content, sizeof(buf));
460 rmmod(buf);
83951d54 461 }
462
707c5824
CP
463 clearmoduledeps();
464
465 if (moddir!=NULL)
466 freesstring(moddir);
467
468 if (modsuffix!=NULL)
469 freesstring(modsuffix);
470
83951d54 471 Error("core",ERR_INFO,"All modules removed. Exiting.");
472}
4e4920a4
CP
473
474/* very slow, make sure you cache the pointer! */
475void *ndlsym(char *modulename, char *fn) {
476 module *mods=(module *)(modules.content);
477 int i=getindex(modulename);
478
479 if (i<0)
480 return NULL;
481
482 return dlsym(mods[i].handle, fn);
483}