LCOV - code coverage report
Current view: top level - src - modmgr.c (source / functions) Hit Total Coverage
Test: mkernel.info Lines: 181 216 83.8 %
Date: 2024-07-29 18:09:36 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /**         @file  modmgr.c
       2             :  *         @brief  Module manager implementation
       3             :  *          @date  25/11/2017
       4             :  *        @author  François Cerbelle (Fanfan), francois@cerbelle.net
       5             :  *     @copyright  Copyright (c) 2017, François Cerbelle
       6             :  *
       7             :  *   @internal
       8             :  *       Compiler  gcc
       9             :  *  Last modified  2024-07-02 09:51
      10             :  *   Organization  Cerbelle.net
      11             :  *        Company  Home
      12             :  *
      13             :  *   This source code is released for free distribution under the terms of the
      14             :  *   GNU General Public License as published by the Free Software Foundation.
      15             :  */
      16             : 
      17             : 
      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             : 
      41             : /** Module list item structure */
      42             : typedef struct modules_s {
      43             :     lt_dlhandle handle;
      44             :     moduleinfo_t* modinfo;
      45             :     struct modules_s *next;
      46             : } modules_t;
      47             : 
      48             : /** Module list head */
      49             : static modules_t* modules = NULL;
      50             : 
      51         867 : static char * dlerrordup (char *errormsg)
      52             : {
      53         867 :     char *error = (char *) lt_dlerror ();
      54         867 :     if (error && !errormsg)
      55         806 :         errormsg = strdup (error);
      56         867 :     return errormsg;
      57             : }
      58             : 
      59         867 : static uint8_t modmgr_error(const char* contextname)
      60             : {
      61         867 :     char* errormsg=NULL;
      62             : 
      63         867 :     errormsg = dlerrordup (errormsg);
      64         867 :     if (NULL != errormsg) {
      65           5 :         fprintf(stderr,"%s: %s\n",contextname,errormsg);
      66           5 :         free(errormsg);
      67           5 :         return 1;
      68             :     }
      69         862 :     DBG_PRINTF("%s: OK",contextname);
      70         862 :     return 0;
      71             : }
      72             : 
      73             : static void modmgr_init_real();
      74             : /** NOOP init function called after real init occured */
      75       13595 : static void modmgr_init_noop()
      76             : {
      77       13595 :     DBG_MSG("modmgr already initialized.");
      78       13595 :     return;
      79             : }
      80             : /** Initialization functor on real init first and then on NOOP version */
      81             : static void (*modmgr_init_ptr)() = modmgr_init_real;
      82             : 
      83             : /** Sanity/Debug check at exit to detect unloaded zombies modules */
      84        2158 : static void modmgr_atexit()
      85             : {
      86        2158 :     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        2158 :     ASSERT((modules==NULL)||(modules->handle==NULL));
      90             :     /* Finished with ltdl now. */
      91        2158 :     if (0!=lt_dlexit ()) {
      92           0 :         modmgr_error("lt_dlexit");
      93             :         /* We should abort here, but aborting in atexit() would be stupid */
      94             :     }
      95        2158 : }
      96             : 
      97             : /** modmgr real init function called executed at the first invocation */
      98        2158 : static void modmgr_init_real()
      99             : {
     100             :     /* Needs to be called once and only once */
     101        2158 :     ASSERT(modules == NULL);
     102             : 
     103        2158 :     DBG_MSG("LTDL initialization");
     104        2158 :     if (0 != lt_dlinit ())
     105           0 :         modmgr_error("lt_dlinit");
     106             : 
     107             :     /* Initialize the module list with a sentinel */
     108        2158 :     DBG_MSG("Module list initialization");
     109        2158 :     modules = (modules_t*)malloc(sizeof(modules_t));
     110        2158 :     if ( NULL == modules ) {
     111           0 :         fprintf(stderr,_("Critical: Module manager lacks RAM (OOM) to initialize.\n"));
     112           0 :         abort();
     113             :     }
     114        2158 :     modules->handle = NULL;
     115        2158 :     modules->modinfo = NULL;
     116        2158 :     modules->next = NULL;
     117        2158 :     DBG_MSG("Register an atexit healthcheck and cleanup fonction");
     118        2158 :     atexit(modmgr_atexit);
     119             : 
     120             :     /* Avoid double calls */
     121        2158 :     modmgr_init_ptr = modmgr_init_noop;
     122        2158 : }
     123             : 
     124             : /** Reset and initialize the modules search path */
     125        3414 : int modmgr_setpath (const char* path)
     126             : {
     127             :     int result;
     128        3414 :     DBG_PRINTF("path=%s",path);
     129        3414 :     modmgr_init_ptr();
     130        3414 :     ASSERT(modules != NULL);
     131             : 
     132             :     /* Set the module search path. */
     133        3414 :     result = lt_dlsetsearchpath (path);
     134        3414 :     if (0!=result)
     135         306 :         modmgr_error("lt_dlsetsearchpath");
     136        3414 :     DBG_PRINTF("search path = %s",lt_dlgetsearchpath());
     137        3414 :     return result;
     138             : }
     139             : 
     140             : /** Add a path at the end (lowest prio) of the search path */
     141          33 : int modmgr_addpath (const char* path)
     142             : {
     143          33 :     int result=0;
     144          33 :     DBG_PRINTF("path=%s",path);
     145          33 :     modmgr_init_ptr();
     146          33 :     ASSERT(modules != NULL);
     147             : 
     148             :     /* Add a module search path. */
     149          33 :     if ((NULL==path)||(0==path[0])) {
     150           0 :         fprintf(stderr,_("Module manager can not add a null or empty path.\n"));
     151             :     } else {
     152          33 :         result = lt_dladdsearchdir (path);
     153          33 :         if (0!=result)
     154           0 :             modmgr_error("lt_dladdsearchdir");
     155             :     }
     156          33 :     DBG_PRINTF("search path = %s",lt_dlgetsearchpath());
     157          33 :     return result;
     158             : }
     159             : 
     160             : /** Insert (with higher prio) a path before the specified one (from the current search path) */
     161        1640 : int modmgr_insertpath (const char* before, const char* path)
     162             : {
     163        1640 :     int result=0;
     164        1640 :     DBG_PRINTF("before=%s, path=%s",before,path);
     165        1640 :     modmgr_init_ptr();
     166        1640 :     ASSERT(modules != NULL);
     167             : 
     168             :     /* Insert a module search path. */
     169        1640 :     if ((NULL==path)||(0==path[0])) {
     170        1109 :         fprintf(stderr,_("Module manager can not insert a null or empty path.\n"));
     171             :     } else {
     172         531 :         result = lt_dlinsertsearchdir (before,path);
     173         531 :         if (0!=result)
     174         495 :             modmgr_error("lt_dlinsertsearchdir");
     175             :     }
     176        1640 :     DBG_PRINTF("search path = %s",lt_dlgetsearchpath());
     177        1640 :     return result;
     178             : }
     179             : 
     180             : /** Get a read-only pointer on the current search path */
     181       10573 : const char* modmgr_getpath ()
     182             : {
     183       10573 :     DBG_TRACE;
     184       10573 :     modmgr_init_ptr();
     185       10573 :     ASSERT(modules != NULL);
     186             : 
     187       10573 :     return lt_dlgetsearchpath();
     188             : }
     189             : 
     190             : /** Load a module, initialize it, add it to the list and return an opaque handle */
     191          18 : modmgr_module_t modmgr_load(const char* modfile)
     192             : {
     193             :     modules_t* module;
     194             :     moduleinfo_t* (*l_onLoad)();
     195             :     modules_t* it;
     196             : 
     197          18 :     DBG_PRINTF("modfile=%s",modfile);
     198          18 :     modmgr_init_ptr();
     199          18 :     ASSERT(modules != NULL);
     200             : 
     201          18 :     if ((NULL==modfile)||(0==modfile[0])) {
     202           0 :         fprintf(stderr,_("Module manager can not load a null or empty modfile.\n"));
     203           0 :         return NULL;
     204             :     }
     205             : 
     206          18 :     module = (modules_t*)malloc(sizeof(modules_t));
     207          18 :     if ( NULL == module ) {
     208             :         /* TRANSLATORS: module filename */
     209           0 :         fprintf(stderr,_("Module manager lacks RAM (OOM) to create a module structure for %s.\n"),modfile);
     210           0 :         return NULL;
     211             :     }
     212             : 
     213          18 :     DBG_PRINTF("Loading module file %s",modfile);
     214          18 :     module->handle = lt_dlopenext (modfile);
     215          18 :     if (NULL==module->handle) {
     216           5 :         modmgr_error("lt_dlopenext");
     217           5 :         fprintf(stderr,_("Module manager can not load %s.\n"),modfile);
     218           5 :         free(module);
     219           5 :         return NULL;
     220             :     }
     221             : 
     222             : #ifndef NDEBUG
     223             :     {
     224          13 :         const lt_dlinfo *li = lt_dlgetinfo(module->handle);
     225          13 :         if (0!=modmgr_error("lt_dlgetinfo")) {
     226           0 :             DBG_MSG("lt_dlgetinfo failed");
     227           0 :             abort();
     228             :         }
     229          13 :         DBG_PRINTF ("ltinfo: filename = %s ", li->filename);
     230          13 :         DBG_PRINTF ("ltinfo: name(ref_count) = %s(%d)", li->name, li->ref_count);
     231             :     }
     232             : #endif
     233             : 
     234          13 :     DBG_PRINTF("Search loaded module (%p) in the module list",module->handle);
     235          13 :     it = modules;
     236          26 :     while ((it)&&(it->handle!=module->handle))
     237          13 :         it = it->next;
     238             : 
     239             :     /* Module already loaded and initialized, free structure and return module */
     240          13 :     if (NULL != it) {
     241           2 :         DBG_PRINTF("Module (%p) already loaded, no initialization",module->handle);
     242           2 :         free(module);
     243           2 :         return it;
     244             :     }
     245             : #ifndef NDEBUG
     246             :     else
     247          11 :         DBG_PRINTF("Module (%p) not yet loaded, keep and initialize",module->handle);
     248             : #endif
     249             : 
     250             :     /* Find the entry points. */
     251          11 :     *(void**)(&l_onLoad) = lt_dlsym (module->handle, "onLoad");
     252             : 
     253             :     /* Mandatory entry point not found, cancel the load */
     254          11 :     if (0!=modmgr_error("lt_dlsym")) {
     255             :         /* TRANSLATORS: module filename */
     256           0 :         fprintf(stderr,_("Module manager can not find the entry point (onLoad) not found (%s).\n"),modfile);
     257             :         /* Unload to decrement the ref counter */
     258           0 :         if (0!=lt_dlclose(module->handle)) {
     259           0 :             modmgr_error("lt_dlclose");
     260             :             /* TRANSLATORS: module filename */
     261           0 :             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           0 :             abort();
     264             :         }
     265           0 :         free(module);
     266           0 :         return NULL;
     267             :     }
     268             : 
     269             :     /* Execute entry point to initialize, if found */
     270          11 :     DBG_PRINTF("Module (%p) entry point found (%p), initializing",module->handle, l_onLoad);
     271          11 :     module->modinfo = l_onLoad();
     272          11 :     if (module->modinfo == NULL) {
     273           0 :         fprintf(stderr,_("Invalid module(%s): entry point (onLoad) did not return module info.\n"),modfile);
     274           0 :         free(module);
     275           0 :         return NULL;
     276             :     }
     277             : 
     278             : #ifndef NDEBUG
     279          11 :     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          11 :     DBG_PRINTF ("modinfo: description = %s",
     285             :                 module->modinfo->moduleDesc);
     286          11 :     DBG_PRINTF ("modinfo: URL = %s",
     287             :                 module->modinfo->moduleURL);
     288          11 :     DBG_PRINTF ("modinfo: author (email) = %s (%s)",
     289             :                 module->modinfo->moduleAuthor,
     290             :                 module->modinfo->moduleEmail);
     291          11 :     DBG_PRINTF ("modinfo: license = %s",
     292             :                 module->modinfo->moduleLicense);
     293             : #endif
     294             : 
     295             :     /* Add module structure to module list */
     296          11 :     DBG_PRINTF("Module (%p) registration in the list",module->handle);
     297             :     /** @todo critical section */
     298          11 :     module->next = modules;
     299          11 :     modules = module;
     300             : 
     301          11 :     DBG_PRINTF("Return the module(%p) handle(%p)",module->handle, module);
     302          11 :     return module;
     303             : }
     304             : 
     305             : /** Decrement the usage counter, if last usage, remove from the module list and call onUnload */
     306          13 : void modmgr_unload(modmgr_module_t module)
     307             : {
     308             :     modules_t *it;
     309             :     modules_t *prevmodule;
     310             :     uint8_t (*l_onUnload)();
     311             : 
     312          13 :     DBG_PRINTF("module(%p)",module);
     313          13 :     modmgr_init_ptr();
     314          13 :     ASSERT(modules != NULL);
     315             : 
     316          13 :     if (NULL==module) {
     317           0 :         fprintf(stderr,_("Module manager can not unload a null module.\n"));
     318           0 :         return;
     319             :     }
     320             : 
     321             :     /* Find the module if loaded */
     322          13 :     it = modules;
     323          13 :     prevmodule = NULL;
     324          15 :     while ((it)&&(module!=it)) {
     325           2 :         prevmodule = it;
     326           2 :         it = it->next;
     327             :     }
     328          13 :     ASSERT(module==it); /* Module not found */
     329             : 
     330             :     {
     331             :         /* Get the reference (loading) counter */
     332             :         const lt_dlinfo *li;
     333          13 :         ASSERT(NULL!=module->handle);
     334          13 :         li = lt_dlgetinfo(module->handle);
     335          13 :         if (0!=modmgr_error("lt_dlgetinfo")) {
     336           0 :             DBG_MSG("lt_dlgetinfo failed");
     337           0 :             fprintf(stderr,_("Critical: Module manager can not get module counter before unload.\n"));
     338           0 :             abort();
     339             :         }
     340          13 :         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          13 :         if (1==li->ref_count) {
     345          11 :             DBG_MSG("Last module usage : call onUnLoad");
     346          11 :             *(void**)(&l_onUnload) = lt_dlsym (it->handle, "onUnload");
     347          11 :             if (0!=modmgr_error("lt_dlsym")) {
     348           0 :                 fprintf(stderr,_("Module manager can not find the module exit point (onUnload).\n"));
     349             :             } else {
     350             :                 uint8_t result;
     351          11 :                 result = 0;
     352             :                 /* Call the exit point function. */
     353          11 :                 result = l_onUnload();
     354          11 :                 if (result != 0)
     355             :                     /* TRANSLATORS: result code, return value */
     356           0 :                     fprintf(stderr,_("Module manager received an error i(%d) from the module exit point (onUnload).\n"),result);
     357          11 :                 DBG_PRINTF ("onUnLoad retCode : %d", result);
     358             :             }
     359             : 
     360             :             /*  Remove module record from module list */
     361          11 :             if (NULL!=prevmodule)
     362           2 :                 prevmodule->next = it->next;
     363             :             else
     364           9 :                 modules = it->next;
     365             :             /* Actually Unload because this was the last usage */
     366          11 :             if (0!=lt_dlclose(module->handle))
     367           0 :                 modmgr_error("lt_dlclose");
     368          11 :             free(it);
     369             :         } else {
     370           2 :             DBG_MSG("Module still used, do not call onUnLoad");
     371             :             /* Unload : Only to decrement the ref counter */
     372           2 :             if (0!=lt_dlclose(module->handle))
     373           0 :                 modmgr_error("lt_dlclose");
     374             :         }
     375             :     }
     376             : }
     377             : 
     378             : /** Output the list of modules */
     379          62 : void modmgr_list()
     380             : {
     381             :     modules_t* it;
     382             : 
     383          62 :     DBG_TRACE;
     384          62 :     modmgr_init_ptr();
     385          62 :     ASSERT(modules != NULL);
     386             : 
     387          62 :     it = modules;
     388          62 :     fprintf(stderr,_("--- Module list : BEGIN ------------------------------\n"));
     389         144 :     while (it) {
     390             :         /* If not on the sentinel */
     391          82 :         if (it->handle) {
     392          40 :             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          20 :                       lt_dlgetinfo(it->handle)->filename,
     400          20 :                       lt_dlgetinfo(it->handle)->name,
     401          20 :                       lt_dlgetinfo(it->handle)->ref_count,
     402          20 :                       it->modinfo->moduleName,
     403          20 :                       it->modinfo->moduleMajor,
     404          20 :                       it->modinfo->moduleMinor,
     405          20 :                       it->modinfo->modulePatch,
     406          20 :                       it->modinfo->moduleDesc,
     407          20 :                       it->modinfo->moduleURL,
     408          20 :                       it->modinfo->moduleAuthor,
     409          20 :                       it->modinfo->moduleEmail,
     410          20 :                       it->modinfo->moduleLicense
     411             :                     );
     412             :         }
     413          82 :         it = it->next;
     414             :     }
     415          62 :     fprintf(stderr,_("--- Module list : END --------------------------------\n"));
     416          62 :     return;
     417             : }
     418             : 
     419             : /** Resolve a module symbol pointer */
     420          18 : void* modmgr_getsymbol(const modmgr_module_t module, const char* szSymbol)
     421             : {
     422             :     modules_t* it;
     423             :     void* pSymbol;
     424             : 
     425          18 :     DBG_PRINTF("module=%p, Symbol=%s",module,szSymbol);
     426             : 
     427          18 :     if (NULL==module) {
     428           5 :         fprintf(stderr,_("Module manager can not resolve a symbol from a null module.\n"));
     429           5 :         return NULL;
     430             :     }
     431             : 
     432          13 :     if ((NULL==szSymbol)||(0==szSymbol[0])) {
     433           0 :         fprintf(stderr,_("Module manager can not resolve a null or empty symbol name.\n"));
     434           0 :         return NULL;
     435             :     }
     436             : 
     437             :     /* Search the module */
     438          13 :     it = modules;
     439          13 :     while ((it)&&(it!=module))
     440           0 :         it = it->next;
     441          13 :     ASSERT(NULL!=it);
     442          13 :     ASSERT(module==it);
     443             : 
     444          13 :     pSymbol = lt_dlsym (it->handle, szSymbol);
     445          13 :     if (0!=modmgr_error("lt_dlsym")) {
     446           0 :         return NULL;
     447             :     }
     448             : 
     449          13 :     return pSymbol;
     450             : }

Generated by: LCOV version 1.16