1 | /* |
1 | #include <svsconfig.h> |
2 | * module.C: Module management. |
2 | #include <common.h> |
3 | * Rights to this code are documented in doc/pod/license.pod. |
3 | #include <ermyth/module.h> |
4 | * |
|
|
5 | * Copyright © 2005-2007 Atheme Project (http://www.atheme.org) |
|
|
6 | */ |
|
|
7 | |
4 | |
8 | static char const rcsid[] = "$Id: module.C,v 1.3 2007/07/21 13:23:22 pippijn Exp $"; |
5 | modules::modules (srcinf const &si, char const * const name, bool norestart, bool (*init)(module *), void (*fini)(), |
9 | |
6 | char const * const vendor, char const * const modversion) |
10 | #include "atheme.h" |
|
|
11 | |
|
|
12 | #include <dlfcn.h> |
|
|
13 | |
|
|
14 | static BlockHeap *module_heap; |
|
|
15 | list_t modules; |
|
|
16 | |
|
|
17 | module_t *modtarget = NULL; |
|
|
18 | |
|
|
19 | void |
|
|
20 | modules_init (void) |
|
|
21 | { |
7 | { |
22 | module_heap = BlockHeapCreate (sizeof (module_t), 256); |
8 | if (provides (name)) |
23 | |
9 | throw module_exception (si, "Trying to register two modules with the same name: %s", name); |
24 | if (!module_heap) |
10 | module *mod = new module (name, norestart, init, fini, vendor, modversion); |
25 | { |
11 | modlist ().push_back (mod); |
26 | slog (LG_ERROR, "modules_init(): block allocator failed."); |
12 | provides (name); |
27 | exit (EXIT_FAILURE); |
|
|
28 | } |
|
|
29 | |
|
|
30 | module_load_dir (MODDIR "/modules"); |
|
|
31 | } |
13 | } |
32 | |
14 | |
33 | /* |
15 | bool |
34 | * module_load() |
16 | modules::provides (char const * const name) |
35 | * |
|
|
36 | * inputs: |
|
|
37 | * a literal filename for a module to load. |
|
|
38 | * |
|
|
39 | * outputs: |
|
|
40 | * the respective module_t object of the module. |
|
|
41 | * |
|
|
42 | * side effects: |
|
|
43 | * a module is loaded and necessary initialization code is run. |
|
|
44 | */ |
|
|
45 | module_t * |
|
|
46 | module_load (char *filespec) |
|
|
47 | { |
17 | { |
48 | node_t *n; |
18 | if (find (name) != NULL) |
49 | module_t *m; |
|
|
50 | v1_moduleheader_t *h; |
|
|
51 | void *handle = NULL; |
|
|
52 | #ifdef HAVE_DLINFO |
|
|
53 | struct link_map *map; |
|
|
54 | #endif |
|
|
55 | |
|
|
56 | if ((m = module_find (filespec))) |
|
|
57 | { |
|
|
58 | slog (LG_INFO, "module_load(): module %s is already loaded [at 0x%lx]", filespec, m->address); |
|
|
59 | return NULL; |
|
|
60 | } |
|
|
61 | |
|
|
62 | handle = linker_open_ext (filespec); |
|
|
63 | |
|
|
64 | if (!handle) |
|
|
65 | { |
|
|
66 | char *errp = sstrdup (dlerror ()); |
|
|
67 | slog (LG_ERROR, "module_load(): error: %s", errp); |
|
|
68 | if (me.connected) |
|
|
69 | snoop (_("MODLOAD:ERROR: loading module \2%s\2: %s"), filespec, errp); |
|
|
70 | free (errp); |
|
|
71 | return NULL; |
|
|
72 | } |
|
|
73 | |
|
|
74 | h = (v1_moduleheader_t *) linker_getsym (handle, "_header"); |
|
|
75 | |
|
|
76 | if (!h) |
|
|
77 | return NULL; |
19 | return true; |
78 | |
|
|
79 | if (h->mod != MAPI_MAGIC) |
|
|
80 | { |
|
|
81 | slog (LG_DEBUG, "module_load(): %s: Attempted to load an incompatible module. Aborting.", filespec); |
|
|
82 | |
|
|
83 | if (me.connected) |
|
|
84 | snoop (_("MODLOAD:ERROR: Module \2%s\2 is not a valid " PACKAGE_NAME " module."), filespec); |
|
|
85 | |
|
|
86 | linker_close (handle); |
|
|
87 | return NULL; |
|
|
88 | } |
|
|
89 | |
|
|
90 | m = static_cast<module_t *> (BlockHeapAlloc (module_heap)); |
|
|
91 | |
|
|
92 | strlcpy (m->modpath, filespec, BUFSIZE); |
|
|
93 | m->handle = handle; |
|
|
94 | m->mflags = MODTYPE_STANDARD; |
|
|
95 | m->header = h; |
|
|
96 | |
|
|
97 | #ifdef HAVE_DLINFO |
|
|
98 | dlinfo (handle, RTLD_DI_LINKMAP, &map); |
|
|
99 | if (map != NULL) |
|
|
100 | m->address = (void *) map->l_addr; |
|
|
101 | else |
|
|
102 | m->address = handle; |
|
|
103 | #else |
|
|
104 | /* best we can do here without dlinfo() --nenolod */ |
|
|
105 | m->address = handle; |
|
|
106 | #endif |
|
|
107 | |
|
|
108 | /* set the module target for module dependencies */ |
|
|
109 | modtarget = m; |
|
|
110 | |
|
|
111 | if (h->modinit) |
|
|
112 | h->modinit (m); |
|
|
113 | |
|
|
114 | /* we won't be loading symbols outside the init code */ |
|
|
115 | modtarget = NULL; |
|
|
116 | |
|
|
117 | if (m->mflags & MODTYPE_FAIL) |
|
|
118 | { |
|
|
119 | slog (LG_ERROR, "module_load(): module %s init failed", filespec); |
|
|
120 | if (me.connected) |
|
|
121 | snoop (_("MODLOAD:ERROR: Init failed while loading module \2%s\2"), filespec); |
|
|
122 | module_unload (m); |
|
|
123 | return NULL; |
|
|
124 | } |
|
|
125 | |
|
|
126 | n = node_create (); |
|
|
127 | node_add (m, n, &modules); |
|
|
128 | |
|
|
129 | slog (LG_DEBUG, "module_load(): loaded %s [at 0x%lx; MAPI version %d]", h->name, m->address, h->abi_ver); |
|
|
130 | |
|
|
131 | if (me.connected && !cold_start) |
|
|
132 | { |
|
|
133 | wallops (_("Module %s loaded [at 0x%lx; MAPI version %d]"), h->name, m->address, h->abi_ver); |
|
|
134 | snoop (_("MODLOAD: \2%s\2 [at 0x%lx; MAPI version %d]"), h->name, m->address, h->abi_ver); |
|
|
135 | } |
|
|
136 | |
|
|
137 | return m; |
20 | return false; |
138 | } |
21 | } |
139 | |
22 | |
140 | /* |
23 | faultcode_t |
141 | * module_load_dir() |
24 | modules::enable (char const * const name) |
142 | * |
|
|
143 | * inputs: |
|
|
144 | * a directory containing modules to load. |
|
|
145 | * |
|
|
146 | * outputs: |
|
|
147 | * none |
|
|
148 | * |
|
|
149 | * side effects: |
|
|
150 | * qualifying modules are passed to module_load(). |
|
|
151 | */ |
|
|
152 | void |
|
|
153 | module_load_dir (char *dirspec) |
|
|
154 | { |
25 | { |
155 | DIR *module_dir = NULL; |
26 | module *m = find (name); |
156 | struct dirent *ldirent = NULL; |
|
|
157 | char module_filename[4096]; |
|
|
158 | |
27 | |
159 | module_dir = opendir (dirspec); |
28 | if (!m) |
|
|
29 | return fault_nosuch_target; |
|
|
30 | |
|
|
31 | if (m->enabled) |
|
|
32 | return fault_nochange; |
160 | |
33 | |
161 | if (!module_dir) |
34 | m->enabled = m->init (m); |
162 | { |
|
|
163 | slog (LG_ERROR, "module_load_dir(): %s: %s", dirspec, strerror (errno)); |
|
|
164 | return; |
|
|
165 | } |
|
|
166 | |
35 | |
167 | while ((ldirent = readdir (module_dir)) != NULL) |
36 | return m->enabled ? fault_ok : fault_failed; |
168 | { |
|
|
169 | if (!strstr (ldirent->d_name, ".so")) |
|
|
170 | continue; |
|
|
171 | |
|
|
172 | snprintf (module_filename, sizeof (module_filename), "%s/%s", dirspec, ldirent->d_name); |
|
|
173 | module_load (module_filename); |
|
|
174 | } |
|
|
175 | |
|
|
176 | closedir (module_dir); |
|
|
177 | } |
37 | } |
178 | |
38 | |
179 | /* |
39 | faultcode_t |
180 | * module_load_dir_match() |
40 | modules::disable (char const * const name) |
181 | * |
|
|
182 | * inputs: |
|
|
183 | * a directory containing modules to load, and a pattern to match against |
|
|
184 | * to determine whether or not a module qualifies for loading. |
|
|
185 | * |
|
|
186 | * outputs: |
|
|
187 | * none |
|
|
188 | * |
|
|
189 | * side effects: |
|
|
190 | * qualifying modules are passed to module_load(). |
|
|
191 | */ |
|
|
192 | void |
|
|
193 | module_load_dir_match (char *dirspec, char *pattern) |
|
|
194 | { |
41 | { |
195 | DIR *module_dir = NULL; |
42 | module *m = find (name); |
196 | struct dirent *ldirent = NULL; |
|
|
197 | char module_filename[4096]; |
|
|
198 | |
43 | |
199 | module_dir = opendir (dirspec); |
44 | if (!m) |
|
|
45 | return fault_nosuch_target; |
200 | |
46 | |
201 | if (!module_dir) |
47 | if (m->norestart) |
202 | { |
48 | return fault_noprivs; |
203 | slog (LG_ERROR, "module_load_dir(): %s: %s", dirspec, strerror (errno)); |
|
|
204 | return; |
|
|
205 | } |
|
|
206 | |
49 | |
207 | while ((ldirent = readdir (module_dir)) != NULL) |
50 | if (m->enabled) |
208 | { |
51 | m->fini (); |
209 | if (!strstr (ldirent->d_name, ".so") && match (pattern, ldirent->d_name)) |
52 | else |
210 | continue; |
53 | return fault_nochange; |
211 | |
54 | |
212 | snprintf (module_filename, sizeof (module_filename), "%s/%s", dirspec, ldirent->d_name); |
55 | m->enabled = false; |
213 | module_load (module_filename); |
|
|
214 | } |
|
|
215 | |
56 | |
216 | closedir (module_dir); |
57 | return fault_ok; |
217 | } |
58 | } |
218 | |
59 | |
219 | /* |
60 | module * |
220 | * module_unload() |
61 | modules::find (char const * const name) |
221 | * |
|
|
222 | * inputs: |
|
|
223 | * a module object to unload. |
|
|
224 | * |
|
|
225 | * outputs: |
|
|
226 | * none |
|
|
227 | * |
|
|
228 | * side effects: |
|
|
229 | * a module is unloaded and neccessary deinitalization code is run. |
|
|
230 | */ |
|
|
231 | void |
|
|
232 | module_unload (module_t *m) |
|
|
233 | { |
62 | { |
234 | node_t *n, *tn; |
63 | foreach (module *m, modlist ()) |
235 | |
|
|
236 | if (!m) |
|
|
237 | return; |
|
|
238 | |
|
|
239 | /* unload modules which depend on us */ |
|
|
240 | LIST_FOREACH_SAFE (n, tn, m->dephost.head) module_unload ((module_t *) n->data); |
|
|
241 | |
|
|
242 | /* let modules that we depend on know that we no longer exist */ |
|
|
243 | LIST_FOREACH_SAFE (n, tn, m->deplist.head) |
|
|
244 | { |
|
|
245 | module_t *hm = (module_t *) n->data; |
|
|
246 | node_t *hn = node_find (m, &hm->dephost); |
|
|
247 | |
|
|
248 | node_del (hn, &hm->dephost); |
|
|
249 | node_free (hn); |
|
|
250 | node_del (n, &m->deplist); |
|
|
251 | node_free (n); |
|
|
252 | } |
|
|
253 | |
|
|
254 | n = node_find (m, &modules); |
|
|
255 | if (n != NULL) |
|
|
256 | { |
|
|
257 | slog (LG_INFO, "module_unload(): unloaded %s", m->header->name); |
|
|
258 | if (me.connected) |
|
|
259 | { |
|
|
260 | wallops (_("Module %s unloaded."), m->header->name); |
|
|
261 | snoop ("MODUNLOAD: \2%s\2", m->header->name); |
|
|
262 | } |
|
|
263 | |
|
|
264 | if (m->header->deinit) |
|
|
265 | m->header->deinit (); |
|
|
266 | node_del (n, &modules); |
|
|
267 | node_free (n); |
|
|
268 | } |
|
|
269 | /* else unloaded in embryonic state */ |
|
|
270 | linker_close (m->handle); |
|
|
271 | BlockHeapFree (module_heap, m); |
|
|
272 | } |
|
|
273 | |
|
|
274 | /* |
|
|
275 | * module_locate_symbol() |
|
|
276 | * |
|
|
277 | * inputs: |
|
|
278 | * a name of a module and a symbol to look for inside it. |
|
|
279 | * |
|
|
280 | * outputs: |
|
|
281 | * the pointer to the module symbol, else NULL if not found. |
|
|
282 | * |
|
|
283 | * side effects: |
|
|
284 | * none |
|
|
285 | */ |
|
|
286 | void * |
|
|
287 | module_locate_symbol (char *modname, char *sym) |
|
|
288 | { |
|
|
289 | module_t *m; |
|
|
290 | void *symptr; |
|
|
291 | |
|
|
292 | if (!(m = module_find_published (modname))) |
|
|
293 | { |
|
|
294 | slog (LG_ERROR, "module_locate_symbol(): %s is not loaded.", modname); |
|
|
295 | return NULL; |
|
|
296 | } |
|
|
297 | |
|
|
298 | if (modtarget != NULL && !node_find (m, &modtarget->deplist)) |
|
|
299 | { |
|
|
300 | slog (LG_DEBUG, "module_locate_symbol(): %s added as a dependency for %s (symbol: %s)", m->header->name, modtarget->header->name, sym); |
|
|
301 | node_add (m, node_create (), &modtarget->deplist); |
|
|
302 | node_add (modtarget, node_create (), &m->dephost); |
|
|
303 | } |
|
|
304 | |
|
|
305 | symptr = linker_getsym (m->handle, sym); |
|
|
306 | |
|
|
307 | if (symptr == NULL) |
|
|
308 | slog (LG_ERROR, "module_locate_symbol(): could not find symbol %s in module %s.", sym, modname); |
|
|
309 | return symptr; |
|
|
310 | } |
|
|
311 | |
|
|
312 | /* |
|
|
313 | * module_find() |
|
|
314 | * |
|
|
315 | * inputs: |
|
|
316 | * a name of a module to locate the object for. |
|
|
317 | * |
|
|
318 | * outputs: |
|
|
319 | * the module object if the module is located, else NULL. |
|
|
320 | * |
|
|
321 | * side effects: |
|
|
322 | * none |
|
|
323 | */ |
|
|
324 | module_t * |
|
|
325 | module_find (char *name) |
|
|
326 | { |
|
|
327 | node_t *n; |
|
|
328 | |
|
|
329 | LIST_FOREACH (n, modules.head) |
|
|
330 | { |
|
|
331 | module_t *m = static_cast<module_t *> (n->data); |
|
|
332 | |
|
|
333 | if (!strcasecmp (m->modpath, name)) |
64 | if (!strcmp (m->name, name)) |
334 | return m; |
65 | return m; |
335 | } |
|
|
336 | |
66 | |
337 | return NULL; |
67 | return NULL; |
338 | } |
68 | } |
339 | |
69 | |
340 | /* |
70 | void |
341 | * module_find_published() |
71 | modules::cleanup () |
342 | * |
|
|
343 | * inputs: |
|
|
344 | * a published (in _header) name of a module to locate the object for. |
|
|
345 | * |
|
|
346 | * outputs: |
|
|
347 | * the module object if the module is located, else NULL. |
|
|
348 | * |
|
|
349 | * side effects: |
|
|
350 | * none |
|
|
351 | */ |
|
|
352 | module_t * |
|
|
353 | module_find_published (char *name) |
|
|
354 | { |
72 | { |
355 | node_t *n; |
73 | list_type::iterator it = modlist().begin (); |
|
|
74 | list_type::iterator et = modlist().end (); |
356 | |
75 | |
357 | LIST_FOREACH (n, modules.head) |
76 | while (--et != it) |
358 | { |
77 | { |
359 | module_t *m = static_cast<module_t *> (n->data); |
78 | module *m = *et; |
360 | |
79 | if (m->enabled) |
361 | if (!strcasecmp (m->header->name, name)) |
80 | m->fini (); |
362 | return m; |
81 | delete m; |
363 | } |
82 | } |
364 | |
|
|
365 | return NULL; |
|
|
366 | } |
83 | } |
367 | |
|
|
368 | /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs |
|
|
369 | * vim:ts=8 |
|
|
370 | * vim:sw=8 |
|
|
371 | * vim:noexpandtab |
|
|
372 | */ |
|
|