LCOV - code coverage report
Current view: top level - libgnucash/gnc-module - gnc-module.c (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 71.5 % 214 153
Test Date: 2026-11-02 18:19:43 Functions: 85.7 % 14 12
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*************************************************************
       2              :  * gnc-module.c -- loadable plugin/module system for gnucash
       3              :  * Copyright 2001 Linux Developers Group, Inc.
       4              :  *************************************************************/
       5              : /********************************************************************\
       6              :  * This program is free software; you can redistribute it and/or    *
       7              :  * modify it under the terms of the GNU General Public License as   *
       8              :  * published by the Free Software Foundation; either version 2 of   *
       9              :  * the License, or (at your option) any later version.              *
      10              :  *                                                                  *
      11              :  * This program is distributed in the hope that it will be useful,  *
      12              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      13              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      14              :  * GNU General Public License for more details.                     *
      15              :  *                                                                  *
      16              :  * You should have received a copy of the GNU General Public License*
      17              :  * along with this program; if not, contact:                        *
      18              :  *                                                                  *
      19              :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      20              :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      21              :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      22              :  *                                                                  *
      23              : \********************************************************************/
      24              : 
      25              : 
      26              : #include <config.h>
      27              : 
      28              : #include <stdio.h>
      29              : #include <stdlib.h>
      30              : #include <string.h>
      31              : #include <gmodule.h>
      32              : #include <sys/types.h>
      33              : #ifdef HAVE_DIRENT_H
      34              : # include <dirent.h>
      35              : #endif
      36              : 
      37              : #include "gnc-module.h"
      38              : 
      39              : static GHashTable * loaded_modules = NULL;
      40              : static GList      * module_info = NULL;
      41              : 
      42              : typedef struct
      43              : {
      44              :     char * module_path;
      45              :     char * module_description;
      46              :     char * module_filepath;
      47              :     int    module_interface;
      48              :     int    module_age;
      49              :     int    module_revision;
      50              : } GNCModuleInfo;
      51              : 
      52              : typedef struct
      53              : {
      54              :     GModule       * gmodule;
      55              :     gchar         * filename;
      56              :     int           load_count;
      57              :     GNCModuleInfo * info;
      58              :     int           (* init_func)(int refcount);
      59              : } GNCLoadedModule;
      60              : 
      61              : static GNCModuleInfo * gnc_module_get_info(const char * lib_path);
      62              : 
      63              : /*************************************************************
      64              :  * gnc_module_system_search_dirs
      65              :  * return a list of dirs to look in for gnc_module libraries
      66              :  *************************************************************/
      67              : 
      68              : static GList *
      69            6 : gnc_module_system_search_dirs(void)
      70              : {
      71            6 :     const char *spath = g_getenv("GNC_MODULE_PATH");
      72            6 :     GList * list    = NULL;
      73            6 :     GString * token = g_string_new(NULL);
      74            6 :     int   escchar   = 0;
      75              :     const char *cpos;
      76              : 
      77            6 :     if (!spath)
      78              :     {
      79            2 :         spath = DEFAULT_MODULE_PATH;
      80              :     }
      81              : 
      82          297 :     for (cpos = spath; *cpos; cpos++)
      83              :     {
      84          291 :         switch (*cpos)
      85              :         {
      86              : #ifndef G_OS_WIN32
      87              :             /* On windows, with '\' as the directory separator character,
      88              :                this additional de-quoting will make every path processing
      89              :                fail miserably. Anyway this should probably be thrown out
      90              :                altogether, because this additional level of de-quoting
      91              :                (after shell quoting) is completely unexpected and
      92              :                uncommon. */
      93            0 :         case '\\':
      94            0 :             if (!escchar)
      95              :             {
      96            0 :                 escchar = TRUE;
      97              :             }
      98              :             else
      99              :             {
     100            0 :                 g_string_append_c(token, *cpos);
     101            0 :                 escchar = FALSE;
     102              :             }
     103            0 :             break;
     104              : #endif
     105              : 
     106              :             /* This is ':' on UNIX machines and ';' under Windows. */
     107            0 :         case G_SEARCHPATH_SEPARATOR:
     108            0 :             if (!escchar)
     109              :             {
     110            0 :                 char *token_str = g_string_free (token, FALSE);
     111            0 :                 list = g_list_append (list, token_str);
     112            0 :                 token = g_string_new(NULL);
     113              :             }
     114              :             else
     115              :             {
     116            0 :                 g_string_append_c(token, *cpos);
     117            0 :                 escchar = FALSE;
     118              :             }
     119            0 :             break;
     120              : 
     121          291 :         default:
     122          291 :             g_string_append_c(token, *cpos);
     123          291 :             escchar = FALSE;
     124          291 :             break;
     125              :         }
     126              :     }
     127            6 :     if (token->len)
     128              :     {
     129            6 :         char *token_str = g_string_free (token, FALSE);
     130            6 :         list = g_list_append(list, token_str);
     131              :     }
     132              :     else
     133              :     {
     134            0 :         g_string_free(token, TRUE);
     135              :     }
     136            6 :     return list;
     137              : }
     138              : 
     139              : /*************************************************************
     140              :  * gnc_module_system_init
     141              :  * initialize the module system
     142              :  *************************************************************/
     143              : void
     144            6 : gnc_module_system_init(void)
     145              : {
     146            6 :     if (loaded_modules)
     147            0 :         return;
     148              : 
     149            6 :     loaded_modules = g_hash_table_new(g_direct_hash, g_direct_equal);
     150              : 
     151              :     /* now crawl the GNC_MODULE_PATH to find likely libraries */
     152            6 :     gnc_module_system_refresh();
     153              : }
     154              : 
     155              : static inline gboolean
     156           13 : exclude_module (const char* module)
     157              : {
     158              :      /* These modules were converted to shared libraries and removed
     159              :       * from GnuCash 4. Simply dlopening them will introduce duplicate
     160              :       * symbols and cause unwanted initializations that may lead to a
     161              :       * crash. See https://bugs.gnucash.org/show_bug.cgi?id=798229.
     162              :       */
     163              :    static const char* excluded_modules[] =
     164              :     {
     165              :         "libgncmod-app-utils",
     166              :         "libgncmod-bi-import",
     167              :         "libgncmod-csv-export",
     168              :         "libgncmod-csv-import",
     169              :         "libgncmod-customer-import",
     170              :         "libgncmod-engine",
     171              :         "libgncmod-generic-import",
     172              :         "libgncmod-gnome-search",
     173              :         "libgncmod-gnome-utils",
     174              :         "libgncmod-html",
     175              :         "libgncmod-ledger-core",
     176              :         "libgncmod-locale-reports-us",
     177              :         "libgncmod-log-replay",
     178              :         "libgncmod-qif-import",
     179              :         "libgncmod-register-core",
     180              :         "libgncmod-register-gnome",
     181              :         "libgncmod-report-gnome",
     182              :         "libgncmod-report-system",
     183              :         "libgncmod-stylesheets",
     184              :         "libgncmod-tax-us"
     185              :     };
     186              :     static const unsigned len = G_N_ELEMENTS (excluded_modules);
     187           13 :     unsigned namelen = strchr(module, '.') ?
     188            0 :         strchr(module, '.') - module : strlen (module);
     189          273 :     for (unsigned i = 0; i < len; ++i)
     190          260 :         if (strncmp(excluded_modules[i], module, MIN(namelen, strlen(excluded_modules[i]))) == 0)
     191            0 :             return TRUE;
     192           13 :     return FALSE;
     193              : }
     194              : 
     195              : /*************************************************************
     196              :  * gnc_module_system_refresh
     197              :  * build the database of modules by looking through the
     198              :  * GNC_MODULE_PATH
     199              :  *************************************************************/
     200              : 
     201              : void
     202            6 : gnc_module_system_refresh(void)
     203              : {
     204              :     GList * search_dirs;
     205              :     GList * current;
     206              : 
     207            6 :     if (!loaded_modules)
     208              :     {
     209            0 :         gnc_module_system_init();
     210              :     }
     211              : 
     212              :     /* get the GNC_MODULE_PATH and split it into directories */
     213            6 :     search_dirs = gnc_module_system_search_dirs();
     214              : 
     215              :     /* look in each search directory */
     216           12 :     for (current = search_dirs; current; current = current->next)
     217              :     {
     218            6 :         GDir *d = g_dir_open(current->data, 0, NULL);
     219            6 :         const gchar *dent = NULL;
     220            6 :         char * fullpath = NULL;
     221              :         GNCModuleInfo * info;
     222              : 
     223            6 :         if (!d) continue;
     224              : 
     225           26 :         while ((dent = g_dir_read_name(d)) != NULL)
     226              :         {
     227              :             /* is the file a loadable module? */
     228              : 
     229              :             /* Gotcha: On MacOS, G_MODULE_SUFFIX is defined as "so",
     230              :              * but if we do not build clean libtool modules with
     231              :              * "-module", we get dynamic libraries ending in .dylib
     232              :              * On Windows, all modules will move to bin/, so they will
     233              :              * be mixed with other libraries, such as gtk+. Adding a
     234              :              * prefix "libgncmod" filter will prevent the module loader
     235              :              * from loading other libraries. The filter should work on
     236              :              * other platforms.
     237              :              */
     238           22 :             if ((g_str_has_suffix(dent, "." G_MODULE_SUFFIX)
     239            3 :                  || g_str_has_suffix(dent, ".dylib"))
     240           19 :                 && g_str_has_prefix(dent, GNC_MODULE_PREFIX)
     241           13 :                 && !exclude_module(dent))
     242              :             {
     243              :                 /* get the full path name, then dlopen the library and see
     244              :                  * if it has the appropriate symbols to be a gnc_module */
     245           13 :                 fullpath = g_build_filename((const gchar *)(current->data),
     246              :                                             dent, (char*)NULL);
     247           13 :                 info     = gnc_module_get_info(fullpath);
     248              : 
     249           13 :                 if (info)
     250              :                 {
     251           12 :                     module_info = g_list_prepend(module_info, info);
     252              :                 }
     253           13 :                 g_free(fullpath);
     254              :             }
     255              :         }
     256            4 :         g_dir_close(d);
     257              : 
     258              :     }
     259              :     /* free the search dir strings */
     260            6 :     g_list_free_full (search_dirs, g_free);
     261            6 : }
     262              : 
     263              : 
     264              : /*************************************************************
     265              :  *  gnc_module_system_modinfo
     266              :  *  return the list of module information
     267              :  *************************************************************/
     268              : 
     269              : GList *
     270            0 : gnc_module_system_modinfo(void)
     271              : {
     272            0 :     if (!loaded_modules)
     273              :     {
     274            0 :         gnc_module_system_init();
     275              :     }
     276              : 
     277            0 :     return module_info;
     278              : }
     279              : 
     280              : 
     281              : /*
     282              :  *  gnc_module_get_symbol
     283              :  *  gets the munged symbol from the file
     284              :  */
     285              : static gboolean
     286           92 : gnc_module_get_symbol(GModule* gmodule, const char* symbol, gpointer res)
     287              : {
     288              :     gchar** strs;
     289              :     gchar* munged_symbol;
     290              :     gchar *basename;
     291              :     gboolean ret;
     292              : 
     293           92 :     g_return_val_if_fail(gmodule, FALSE);
     294           92 :     g_return_val_if_fail(symbol, FALSE);
     295              : 
     296              :     /* Separate the file from its extension */
     297              :     /* Note: This currently does not work with versioned libtool dlls,
     298              :      * as they are named like libgncmodbaz-0.dll */
     299           92 :     basename = g_path_get_basename(g_module_name(gmodule));
     300           92 :     strs = g_strsplit(basename, ".", 2);
     301           92 :     g_free(basename);
     302              : 
     303              :     /* Translate any dashes to underscores */
     304           92 :     g_strdelimit(strs[0], "-", '_');
     305              : 
     306              :     /* Create the symbol <filename>_<symbol> and retrieve that symbol */
     307           92 :     munged_symbol = g_strdup_printf("%s_%s", strs[0], symbol);
     308           92 :     ret = g_module_symbol(gmodule, munged_symbol, res);
     309              : 
     310              :     /* printf("(%d) Looking for symbol %s\n", ret, munged_symbol); */
     311              : 
     312              :     /* Free everything */
     313           92 :     g_strfreev(strs);
     314           92 :     g_free(munged_symbol);
     315           92 :     return ret;
     316              : }
     317              : 
     318              : /*************************************************************
     319              :  *  gnc_module_get_info
     320              :  *  check a proposed gnc_module by looking for specific symbols in it;
     321              :  *  if it's a gnc_module, return a struct describing it.
     322              :  *************************************************************/
     323              : 
     324              : static GNCModuleInfo *
     325           13 : gnc_module_get_info(const char * fullpath)
     326              : {
     327              :     GModule *gmodule;
     328              :     gpointer modsysver;
     329           13 :     GNCModuleInfo *info = NULL;
     330              :     gpointer initfunc, pathfunc, descripfunc, iface, revision, age;
     331              :     gchar * (* f_path)(void);
     332              :     gchar * (* f_descrip)(void);
     333              : 
     334              :     /*   g_debug("(init) dlopening '%s'\n", fullpath); */
     335           13 :     gmodule = g_module_open(fullpath, G_MODULE_BIND_LAZY);
     336           13 :     if (gmodule == NULL)
     337              :     {
     338            0 :         g_debug("Failed to dlopen() '%s': %s\n", fullpath, g_module_error());
     339            0 :         return NULL;
     340              :     }
     341              : 
     342              :     /* the modsysver tells us what the expected symbols and their
     343              :      * types are */
     344           13 :     if (!gnc_module_get_symbol(gmodule, "gnc_module_system_interface", &modsysver))
     345              :     {
     346              :         /*       g_debug("Module '%s' does not contain 'gnc_module_system_interface'\n", */
     347              :         /*                 fullpath); */
     348            0 :         goto get_info_close;
     349              :     }
     350              : 
     351           13 :     if (*(int *)modsysver != 0)
     352              :     {
     353            1 :         g_warning("Module '%s' requires newer module system\n", fullpath);
     354            1 :         goto get_info_close;
     355              :     }
     356              : 
     357           24 :     if (!gnc_module_get_symbol(gmodule, "gnc_module_init", &initfunc) ||
     358           24 :             !gnc_module_get_symbol(gmodule, "gnc_module_path", &pathfunc) ||
     359           24 :             !gnc_module_get_symbol(gmodule, "gnc_module_description", &descripfunc) ||
     360           24 :             !gnc_module_get_symbol(gmodule, "gnc_module_current", &iface) ||
     361           24 :             !gnc_module_get_symbol(gmodule, "gnc_module_revision", &revision) ||
     362           12 :             !gnc_module_get_symbol(gmodule, "gnc_module_age", &age))
     363              :     {
     364            0 :         g_warning("Module '%s' does not match module signature\n", fullpath);
     365            0 :         goto get_info_close;
     366              :     }
     367              : 
     368              :     /* we have found a gnc_module. */
     369           12 :     info = g_new0(GNCModuleInfo, 1);
     370           12 :     f_path                   = pathfunc;
     371           12 :     f_descrip                = descripfunc;
     372           12 :     info->module_path        = f_path();
     373           12 :     info->module_description = f_descrip();
     374           12 :     info->module_filepath    = g_strdup(fullpath);
     375           12 :     info->module_interface   = *(int *)iface;
     376           12 :     info->module_age         = *(int *)age;
     377           12 :     info->module_revision    = *(int *)revision;
     378              : 
     379           12 :     g_module_make_resident(gmodule);
     380           13 : get_info_close:
     381              :     /*   g_debug("(init) closing '%s'\n", fullpath); */
     382           13 :     g_module_close(gmodule);
     383              : 
     384           13 :     return info;
     385              : }
     386              : 
     387              : 
     388              : /*************************************************************
     389              :  * gnc_module_locate
     390              :  * find the best matching module for the name, interface pair
     391              :  *************************************************************/
     392              : 
     393              : static GNCModuleInfo *
     394           14 : gnc_module_locate(const gchar * module_name, int iface)
     395              : {
     396           14 :     GNCModuleInfo * best    = NULL;
     397           14 :     GNCModuleInfo * current = NULL;
     398              :     GList * lptr;
     399              : 
     400           14 :     if (!loaded_modules)
     401              :     {
     402            0 :         gnc_module_system_init();
     403              :     }
     404              : 
     405           62 :     for (lptr = module_info; lptr; lptr = lptr->next)
     406              :     {
     407           48 :         current = lptr->data;
     408           48 :         if (!strcmp(module_name, current->module_path) &&
     409           12 :                 (iface >= (current->module_interface - current->module_age)) &&
     410           12 :                 (iface <= current->module_interface))
     411              :         {
     412           10 :             if (best)
     413              :             {
     414            0 :                 if ((current->module_interface > best->module_interface) ||
     415            0 :                         ((current->module_interface == best->module_interface) &&
     416            0 :                          (current->module_age > best->module_age)) ||
     417            0 :                         ((current->module_interface == best->module_interface) &&
     418            0 :                          (current->module_age == best->module_age) &&
     419            0 :                          (current->module_revision > best->module_revision)))
     420              :                 {
     421            0 :                     best = current;
     422              :                 }
     423              :             }
     424              :             else
     425              :             {
     426           10 :                 best = current;
     427              :             }
     428              :         }
     429              :     }
     430           14 :     return best;
     431              : }
     432              : 
     433              : static void
     434            1 : list_loaded (gpointer k, gpointer v, gpointer data)
     435              : {
     436            1 :     GList ** l = data;
     437            1 :     *l = g_list_prepend(*l, v);
     438            1 : }
     439              : 
     440              : static GNCLoadedModule *
     441            7 : gnc_module_check_loaded(const char * module_name, gint iface)
     442              : {
     443            7 :     GNCModuleInfo * modinfo = gnc_module_locate(module_name, iface);
     444            7 :     GList * modules = NULL;
     445            7 :     GList * p = NULL;
     446            7 :     GNCLoadedModule * rv = NULL;
     447              : 
     448            7 :     if (modinfo == NULL)
     449              :     {
     450            2 :         return NULL;
     451              :     }
     452              : 
     453            5 :     if (!loaded_modules)
     454              :     {
     455            0 :         gnc_module_system_init();
     456              :     }
     457              : 
     458              :     /* turn the loaded-modules table into a list */
     459            5 :     g_hash_table_foreach(loaded_modules, list_loaded, &modules);
     460              : 
     461              :     /* walk the list to see if the file we want is already open */
     462            6 :     for (p = modules; p; p = p->next)
     463              :     {
     464            1 :         GNCLoadedModule * lm = p->data;
     465            1 :         if (!strcmp(lm->filename, modinfo->module_filepath))
     466              :         {
     467            0 :             rv = lm;
     468            0 :             break;
     469              :         }
     470              :     }
     471            5 :     g_list_free(modules);
     472            5 :     return rv;
     473              : }
     474              : 
     475              : 
     476              : /*************************************************************
     477              :  * gnc_module_load
     478              :  * Ensure that the module named by "module_name" is loaded.
     479              :  *************************************************************/
     480              : 
     481              : static GNCModule
     482            7 : gnc_module_load_common(const char * module_name, gint iface, gboolean optional)
     483              : {
     484              : 
     485              :     GNCLoadedModule * info;
     486              :     GModule         * gmodule;
     487              :     GNCModuleInfo   * modinfo;
     488              : 
     489            7 :     g_debug ("module_name: %s", module_name);
     490              : 
     491            7 :     if (!loaded_modules)
     492              :     {
     493            0 :         gnc_module_system_init();
     494              :     }
     495              : 
     496            7 :     info = gnc_module_check_loaded(module_name, iface);
     497              : 
     498              :     /* if the module's already loaded, just increment its use count.
     499              :      * otherwise, load it and check for the initializer
     500              :      * "gnc_module_init".  if we find that, assume it's a gnucash module
     501              :      * and run the function. */
     502              : 
     503            7 :     if (info)
     504              :     {
     505              :         /* module already loaded ... call the init thunk */
     506            0 :         if (info->init_func)
     507              :         {
     508            0 :             if (info->init_func(info->load_count))
     509              :             {
     510            0 :                 info->load_count++;
     511            0 :                 g_debug ("module %s already loaded", module_name);
     512            0 :                 return info;
     513              :             }
     514              :             else
     515              :             {
     516            0 :                 g_warning ("module init failed: %s", module_name);
     517            0 :                 return NULL;
     518              :             }
     519              :         }
     520              :         else
     521              :         {
     522            0 :             g_warning ("module has no init func: %s", module_name);
     523            0 :             return NULL;
     524              :         }
     525              :         /* NOTREACHED */
     526              :         g_error("internal error");
     527              :         return NULL;
     528              :     }
     529              : 
     530            7 :     modinfo = gnc_module_locate(module_name, iface);
     531            7 :     if (!modinfo)
     532              :     {
     533            2 :         if (optional)
     534              :         {
     535            0 :             g_message ("Could not locate optional module %s interface v.%d",
     536              :                        module_name, iface);
     537              :         }
     538              :         else
     539              :         {
     540            2 :             g_warning ("Could not locate module %s interface v.%d",
     541              :                        module_name, iface);
     542              :         }
     543            2 :         return NULL;
     544              :     }
     545              : 
     546              :     /*     if (modinfo) */
     547              :     /*       g_debug("(init) loading '%s' from '%s'\n", module_name, */
     548              :     /*               modinfo->module_filepath); */
     549              : 
     550            5 :     if ((gmodule = g_module_open(modinfo->module_filepath, 0)) != NULL)
     551              :     {
     552              :         gpointer initfunc;
     553              : 
     554            5 :         if (gnc_module_get_symbol(gmodule, "gnc_module_init", &initfunc))
     555              :         {
     556              :             /* stick it in the hash table */
     557            5 :             info = g_new0(GNCLoadedModule, 1);
     558            5 :             info->gmodule    = gmodule;
     559            5 :             info->filename   = g_strdup(modinfo->module_filepath);
     560            5 :             info->load_count = 1;
     561            5 :             info->init_func  = initfunc;
     562            5 :             g_hash_table_insert(loaded_modules, info, info);
     563              : 
     564              :             /* now call its init function.  this should load any dependent
     565              :              * modules, too.  If it doesn't return TRUE unload the module. */
     566            5 :             if (!info->init_func(0))
     567              :             {
     568              :                 /* init failed. unload the module. */
     569            1 :                 g_warning ("Initialization failed for module %s", module_name);
     570            1 :                 g_hash_table_remove(loaded_modules, info);
     571            1 :                 g_free(info->filename);
     572            1 :                 g_free(info);
     573              :                 /* g_module_close(module); */
     574            1 :                 return NULL;
     575              :             }
     576              : 
     577            4 :             return info;
     578              :         }
     579              :         else
     580              :         {
     581            0 :             g_warning ("Module %s (%s) is not a gnc-module.\n", module_name,
     582              :                        modinfo->module_filepath);
     583              :             //lt_dlclose(handle);
     584              :         }
     585            0 :         return info;
     586              :     }
     587              : 
     588            0 :     g_warning ("Failed to open module %s: %s\n", module_name, g_module_error());
     589              : 
     590            0 :     return NULL;
     591              : }
     592              : 
     593              : 
     594              : GNCModule
     595            7 : gnc_module_load(const char * module_name, gint iface)
     596              : {
     597            7 :     return gnc_module_load_common(module_name, iface, FALSE);
     598              : }
     599              : 
     600              : GNCModule
     601            0 : gnc_module_load_optional(const char * module_name, gint iface)
     602              : {
     603            0 :     return gnc_module_load_common(module_name, iface, TRUE);
     604              : }
     605              : 
     606              : /*************************************************************
     607              :  * gnc_module_unload
     608              :  * unload a module (only actually unload it if the use count goes to 0)
     609              :  *************************************************************/
     610              : 
     611              : int
     612            2 : gnc_module_unload(GNCModule module)
     613              : {
     614              :     GNCLoadedModule * info;
     615              : 
     616            2 :     if (!loaded_modules)
     617              :     {
     618            0 :         gnc_module_system_init();
     619              :     }
     620              : 
     621            2 :     if ((info = g_hash_table_lookup(loaded_modules, module)) != NULL)
     622              :     {
     623              :         gpointer unload_thunk;
     624            2 :         int unload_val = TRUE;
     625              : 
     626            2 :         info->load_count--;
     627            2 :         if (gnc_module_get_symbol(info->gmodule, "gnc_module_end", &unload_thunk))
     628              :         {
     629            0 :             int (* thunk)(int) = unload_thunk;
     630            0 :             unload_val = thunk(info->load_count);
     631              :         }
     632              : 
     633              :         /* actually unload the module if necessary */
     634            2 :         if (info->load_count == 0)
     635              :         {
     636              :             /* now close the module and free the struct */
     637              :             /* g_debug("(unload) closing %s\n", info->filename); */
     638              :             /* g_module_close(info->gmodule); */
     639            2 :             g_hash_table_remove(loaded_modules, module);
     640            2 :             g_free(info);
     641              :         }
     642            2 :         return unload_val;
     643              :     }
     644              :     else
     645              :     {
     646            0 :         g_warning ("Failed to unload module %p (it is not loaded)\n", module);
     647            0 :         return 0;
     648              :     }
     649              : }
     650              : 
        

Generated by: LCOV version 2.0-1