mkernel 0.0.2
Micro-kernel framework, everything as a module
modmgr.c
Go to the documentation of this file.
1
18#include "modmgr.h"
19#include "gettext.h"
20#define _(String) gettext (String)
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include <limits.h>
27#ifndef PATH_MAX
28# define PATH_MAX 255
29#endif
30
31#include <string.h>
32#include <ltdl.h>
33
34#if LIBPTHREAD
35# include <pthread.h>
36#endif
37
38#include "debug/assert.h"
39#include "debug/memory.h"
40
42typedef struct modules_s {
43 lt_dlhandle handle;
45 struct modules_s *next;
47
49static modules_t* modules = NULL;
50
51static char * dlerrordup (char *errormsg)
52{
53 char *error = (char *) lt_dlerror ();
54 if (error && !errormsg)
55 errormsg = strdup (error);
56 return errormsg;
57}
58
59static uint8_t modmgr_error(const char* contextname)
60{
61 char* errormsg=NULL;
62
63 errormsg = dlerrordup (errormsg);
64 if (NULL != errormsg) {
65 fprintf(stderr,"%s: %s\n",contextname,errormsg);
66 free(errormsg);
67 return 1;
68 }
69 DBG_PRINTF("%s: OK",contextname);
70 return 0;
71}
72
73static void modmgr_init_real();
75static void modmgr_init_noop()
76{
77 DBG_MSG("modmgr already initialized.");
78 return;
79}
81static void (*modmgr_init_ptr)() = modmgr_init_real;
82
84static void modmgr_atexit()
85{
86 DBG_MSG("Validate that module list is empty.");
87 /* Checks that all modules were unloaded before exit */
88 /* Module list should be either not initialized or with sentinel only */
89 ASSERT((modules==NULL)||(modules->handle==NULL));
90 /* Finished with ltdl now. */
91 if (0!=lt_dlexit ()) {
92 modmgr_error("lt_dlexit");
93 /* We should abort here, but aborting in atexit() would be stupid */
94 }
95}
96
98static void modmgr_init_real()
99{
100 /* Needs to be called once and only once */
101 ASSERT(modules == NULL);
102
103 DBG_MSG("LTDL initialization");
104 if (0 != lt_dlinit ())
105 modmgr_error("lt_dlinit");
106
107 /* Initialize the module list with a sentinel */
108 DBG_MSG("Module list initialization");
109 modules = (modules_t*)malloc(sizeof(modules_t));
110 if ( NULL == modules ) {
111 fprintf(stderr,_("Critical: Module manager lacks RAM (OOM) to initialize.\n"));
112 abort();
113 }
114 modules->handle = NULL;
115 modules->modinfo = NULL;
116 modules->next = NULL;
117 DBG_MSG("Register an atexit healthcheck and cleanup fonction");
118 atexit(modmgr_atexit);
119
120 /* Avoid double calls */
121 modmgr_init_ptr = modmgr_init_noop;
122}
123
125int modmgr_setpath (const char* path)
126{
127 int result;
128 DBG_PRINTF("path=%s",path);
129 modmgr_init_ptr();
130 ASSERT(modules != NULL);
131
132 /* Set the module search path. */
133 result = lt_dlsetsearchpath (path);
134 if (0!=result)
135 modmgr_error("lt_dlsetsearchpath");
136 DBG_PRINTF("search path = %s",lt_dlgetsearchpath());
137 return result;
138}
139
141int modmgr_addpath (const char* path)
142{
143 int result=0;
144 DBG_PRINTF("path=%s",path);
145 modmgr_init_ptr();
146 ASSERT(modules != NULL);
147
148 /* Add a module search path. */
149 if ((NULL==path)||(0==path[0])) {
150 fprintf(stderr,_("Module manager can not add a null or empty path.\n"));
151 } else {
152 result = lt_dladdsearchdir (path);
153 if (0!=result)
154 modmgr_error("lt_dladdsearchdir");
155 }
156 DBG_PRINTF("search path = %s",lt_dlgetsearchpath());
157 return result;
158}
159
161int modmgr_insertpath (const char* before, const char* path)
162{
163 int result=0;
164 DBG_PRINTF("before=%s, path=%s",before,path);
165 modmgr_init_ptr();
166 ASSERT(modules != NULL);
167
168 /* Insert a module search path. */
169 if ((NULL==path)||(0==path[0])) {
170 fprintf(stderr,_("Module manager can not insert a null or empty path.\n"));
171 } else {
172 result = lt_dlinsertsearchdir (before,path);
173 if (0!=result)
174 modmgr_error("lt_dlinsertsearchdir");
175 }
176 DBG_PRINTF("search path = %s",lt_dlgetsearchpath());
177 return result;
178}
179
181const char* modmgr_getpath ()
182{
183 DBG_TRACE;
184 modmgr_init_ptr();
185 ASSERT(modules != NULL);
186
187 return lt_dlgetsearchpath();
188}
189
191modmgr_module_t modmgr_load(const char* modfile)
192{
193 modules_t* module;
194 moduleinfo_t* (*l_onLoad)();
195 modules_t* it;
196
197 DBG_PRINTF("modfile=%s",modfile);
198 modmgr_init_ptr();
199 ASSERT(modules != NULL);
200
201 if ((NULL==modfile)||(0==modfile[0])) {
202 fprintf(stderr,_("Module manager can not load a null or empty modfile.\n"));
203 return NULL;
204 }
205
206 module = (modules_t*)malloc(sizeof(modules_t));
207 if ( NULL == module ) {
208 /* TRANSLATORS: module filename */
209 fprintf(stderr,_("Module manager lacks RAM (OOM) to create a module structure for %s.\n"),modfile);
210 return NULL;
211 }
212
213 DBG_PRINTF("Loading module file %s",modfile);
214 module->handle = lt_dlopenext (modfile);
215 if (NULL==module->handle) {
216 modmgr_error("lt_dlopenext");
217 fprintf(stderr,_("Module manager can not load %s.\n"),modfile);
218 free(module);
219 return NULL;
220 }
221
222#ifndef NDEBUG
223 {
224 const lt_dlinfo *li = lt_dlgetinfo(module->handle);
225 if (0!=modmgr_error("lt_dlgetinfo")) {
226 DBG_MSG("lt_dlgetinfo failed");
227 abort();
228 }
229 DBG_PRINTF ("ltinfo: filename = %s ", li->filename);
230 DBG_PRINTF ("ltinfo: name(ref_count) = %s(%d)", li->name, li->ref_count);
231 }
232#endif
233
234 DBG_PRINTF("Search loaded module (%p) in the module list",module->handle);
235 it = modules;
236 while ((it)&&(it->handle!=module->handle))
237 it = it->next;
238
239 /* Module already loaded and initialized, free structure and return module */
240 if (NULL != it) {
241 DBG_PRINTF("Module (%p) already loaded, no initialization",module->handle);
242 free(module);
243 return it;
244 }
245#ifndef NDEBUG
246 else
247 DBG_PRINTF("Module (%p) not yet loaded, keep and initialize",module->handle);
248#endif
249
250 /* Find the entry points. */
251 *(void**)(&l_onLoad) = lt_dlsym (module->handle, "onLoad");
252
253 /* Mandatory entry point not found, cancel the load */
254 if (0!=modmgr_error("lt_dlsym")) {
255 /* TRANSLATORS: module filename */
256 fprintf(stderr,_("Module manager can not find the entry point (onLoad) not found (%s).\n"),modfile);
257 /* Unload to decrement the ref counter */
258 if (0!=lt_dlclose(module->handle)) {
259 modmgr_error("lt_dlclose");
260 /* TRANSLATORS: module filename */
261 fprintf(stderr,_("Critical: Module manager can not unload partially loaded invalid module (%s).\n"),modfile);
262 /* Do not cleanup, immediate abort to help debugging */
263 abort();
264 }
265 free(module);
266 return NULL;
267 }
268
269 /* Execute entry point to initialize, if found */
270 DBG_PRINTF("Module (%p) entry point found (%p), initializing",module->handle, l_onLoad);
271 module->modinfo = l_onLoad();
272 if (module->modinfo == NULL) {
273 fprintf(stderr,_("Invalid module(%s): entry point (onLoad) did not return module info.\n"),modfile);
274 free(module);
275 return NULL;
276 }
277
278#ifndef NDEBUG
279 DBG_PRINTF ("modinfo: name (version) = %s (v%d.%d.%d)",
280 module->modinfo->moduleName,
281 module->modinfo->moduleMajor,
282 module->modinfo->moduleMinor,
283 module->modinfo->modulePatch);
284 DBG_PRINTF ("modinfo: description = %s",
285 module->modinfo->moduleDesc);
286 DBG_PRINTF ("modinfo: URL = %s",
287 module->modinfo->moduleURL);
288 DBG_PRINTF ("modinfo: author (email) = %s (%s)",
289 module->modinfo->moduleAuthor,
290 module->modinfo->moduleEmail);
291 DBG_PRINTF ("modinfo: license = %s",
292 module->modinfo->moduleLicense);
293#endif
294
295 /* Add module structure to module list */
296 DBG_PRINTF("Module (%p) registration in the list",module->handle);
298 module->next = modules;
299 modules = module;
300
301 DBG_PRINTF("Return the module(%p) handle(%p)",module->handle, module);
302 return module;
303}
304
307{
308 modules_t *it;
309 modules_t *prevmodule;
310 uint8_t (*l_onUnload)();
311
312 DBG_PRINTF("module(%p)",module);
313 modmgr_init_ptr();
314 ASSERT(modules != NULL);
315
316 if (NULL==module) {
317 fprintf(stderr,_("Module manager can not unload a null module.\n"));
318 return;
319 }
320
321 /* Find the module if loaded */
322 it = modules;
323 prevmodule = NULL;
324 while ((it)&&(module!=it)) {
325 prevmodule = it;
326 it = it->next;
327 }
328 ASSERT(module==it); /* Module not found */
329
330 {
331 /* Get the reference (loading) counter */
332 const lt_dlinfo *li;
333 ASSERT(NULL!=module->handle);
334 li = lt_dlgetinfo(module->handle);
335 if (0!=modmgr_error("lt_dlgetinfo")) {
336 DBG_MSG("lt_dlgetinfo failed");
337 fprintf(stderr,_("Critical: Module manager can not get module counter before unload.\n"));
338 abort();
339 }
340 DBG_PRINTF ("ltinfo = %s (%s:%d)",
341 li->filename, li->name,
342 li->ref_count);
343 /* Execute onUnLoad only if really unloading the very last occurence */
344 if (1==li->ref_count) {
345 DBG_MSG("Last module usage : call onUnLoad");
346 *(void**)(&l_onUnload) = lt_dlsym (it->handle, "onUnload");
347 if (0!=modmgr_error("lt_dlsym")) {
348 fprintf(stderr,_("Module manager can not find the module exit point (onUnload).\n"));
349 } else {
350 uint8_t result;
351 result = 0;
352 /* Call the exit point function. */
353 result = l_onUnload();
354 if (result != 0)
355 /* TRANSLATORS: result code, return value */
356 fprintf(stderr,_("Module manager received an error i(%d) from the module exit point (onUnload).\n"),result);
357 DBG_PRINTF ("onUnLoad retCode : %d", result);
358 }
359
360 /* Remove module record from module list */
361 if (NULL!=prevmodule)
362 prevmodule->next = it->next;
363 else
364 modules = it->next;
365 /* Actually Unload because this was the last usage */
366 if (0!=lt_dlclose(module->handle))
367 modmgr_error("lt_dlclose");
368 free(it);
369 } else {
370 DBG_MSG("Module still used, do not call onUnLoad");
371 /* Unload : Only to decrement the ref counter */
372 if (0!=lt_dlclose(module->handle))
373 modmgr_error("lt_dlclose");
374 }
375 }
376}
377
380{
381 modules_t* it;
382
383 DBG_TRACE;
384 modmgr_init_ptr();
385 ASSERT(modules != NULL);
386
387 it = modules;
388 printf(_("--- Module list :\n"));
389 while (it) {
390 /* If not on the sentinel */
391 if (it->handle) {
392 fprintf ( stderr,
393 "ltinfo = %s (%s:%d), "
394 "name (version) = %s (v%d.%d.%d), "
395 "description = %s, "
396 "URL = %s, "
397 "author (email) = %s (%s), "
398 "license = %s\n",
399 lt_dlgetinfo(it->handle)->filename,
400 lt_dlgetinfo(it->handle)->name,
401 lt_dlgetinfo(it->handle)->ref_count,
402 it->modinfo->moduleName,
403 it->modinfo->moduleMajor,
404 it->modinfo->moduleMinor,
405 it->modinfo->modulePatch,
406 it->modinfo->moduleDesc,
407 it->modinfo->moduleURL,
409 it->modinfo->moduleEmail,
411 );
412 }
413 it = it->next;
414 }
415 return;
416}
417
419void* modmgr_getsymbol(const modmgr_module_t module, const char* szSymbol)
420{
421 modules_t* it;
422 void* pSymbol;
423
424 DBG_PRINTF("module=%p, Symbol=%s",module,szSymbol);
425
426 if (NULL==module) {
427 fprintf(stderr,_("Module manager can not resolve a symbol from a null module.\n"));
428 return NULL;
429 }
430
431 if ((NULL==szSymbol)||(0==szSymbol[0])) {
432 fprintf(stderr,_("Module manager can not resolve a null or empty symbol name.\n"));
433 return NULL;
434 }
435
436 /* Search the module */
437 it = modules;
438 while ((it)&&(it!=module))
439 it = it->next;
440 ASSERT(NULL!=it);
441 ASSERT(module==it);
442
443 pSymbol = lt_dlsym (it->handle, szSymbol);
444 if (0!=modmgr_error("lt_dlsym")) {
445 return NULL;
446 }
447
448 return pSymbol;
449}
Debugging macros.
#define DBG_TRACE
Checkpoint on stderr.
Definition: assert.h:118
#define DBG_PRINTF(p_Format,...)
Log a timestamped debugging message on stderr.
Definition: assert.h:155
#define ASSERT(condition)
Assertion check macro.
Definition: assert.h:103
#define DBG_MSG(msg)
Checkpoint on stderr with a static message.
Definition: assert.h:130
Tracks memory allocation and leaks when compiled without NDEBUG.
#define malloc(size)
Same syntaxt and same behavior than regular malloc function, with memory leaks tracking.
Definition: memory.h:32
#define strdup(chaine)
Same syntaxt and same behavior than regular strdup function, with memory leaks tracking.
Definition: memory.h:44
#define free(ptr)
Same syntaxt and same behavior than regular free function, with memory leaks tracking.
Definition: memory.h:41
#define NULL
Definition: mkernel-opt.c:64
int modmgr_setpath(const char *path)
Reset and initialize the modules search path.
Definition: modmgr.c:125
#define _(String)
Definition: modmgr.c:20
void * modmgr_getsymbol(const modmgr_module_t module, const char *szSymbol)
Resolve a module symbol pointer.
Definition: modmgr.c:419
int modmgr_insertpath(const char *before, const char *path)
Insert (with higher prio) a path before the specified one (from the current search path)
Definition: modmgr.c:161
void modmgr_list()
Output the list of modules.
Definition: modmgr.c:379
modmgr_module_t modmgr_load(const char *modfile)
Load a module, initialize it, add it to the list and return an opaque handle.
Definition: modmgr.c:191
int modmgr_addpath(const char *path)
Add a path at the end (lowest prio) of the search path.
Definition: modmgr.c:141
void modmgr_unload(modmgr_module_t module)
Decrement the usage counter, if last usage, remove from the module list and call onUnload.
Definition: modmgr.c:306
const char * modmgr_getpath()
Get a read-only pointer on the current search path.
Definition: modmgr.c:181
struct modules_s modules_t
Module list item structure.
Module manager headers.
const char * moduleDesc
Definition: module.h:29
const char * moduleAuthor
Definition: module.h:33
const char * moduleEmail
Definition: module.h:34
const uint8_t modulePatch
Definition: module.h:32
const uint8_t moduleMinor
Definition: module.h:31
const char * moduleURL
Definition: module.h:35
const uint8_t moduleMajor
Definition: module.h:30
const char * moduleLicense
Definition: module.h:36
const char * moduleName
Definition: module.h:28
Module list item structure.
Definition: modmgr.c:42
struct modules_s * next
Definition: modmgr.c:45
moduleinfo_t * modinfo
Definition: modmgr.c:44
lt_dlhandle handle
Definition: modmgr.c:43