Mercurial > ~mikael > mcabber > hg
view mcabber/mcabber/modules.c @ 2225:dc3b3ac1ba76
Free the buffdata structures when buffers are closed
Free the buffdata strcutures when buffers are closed and there are no
more users (these structures can be shared if the "symlink" shared history
is used).
author | Mikael Berthe <mikael@lilotux.net> |
---|---|
date | Sat, 07 Nov 2015 12:21:12 +0100 |
parents | 78616d66a7f2 |
children | f5402d705f67 |
line wrap: on
line source
/* * modules.c -- Modules handling * * Copyright (C) 2010 Myhailo Danylenko <isbear@ukrpost.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #include <glib.h> #include <gmodule.h> #include <string.h> #include "settings.h" #include "config.h" #include "modules.h" #include "screen.h" #include "utils.h" // Registry of loaded modules GSList *loaded_modules = NULL; const gchar *mcabber_branch = MCABBER_BRANCH; const guint mcabber_api_version = MCABBER_API_VERSION; static gint module_list_comparator(gconstpointer arg1, gconstpointer arg2) { const loaded_module_t *module = arg1; const char *name = arg2; return g_strcmp0(module->name, name); } // module_load(modulename, manual, force) // Tries to load specified module and any modules, that this module // depends on. Returns NULL on success or constant error string in a // case of error. Error message not necessarily indicates error. const gchar *module_load(const gchar *arg, gboolean manual, gboolean force) { GModule *mod; module_info_t *info; if (!arg || !*arg) return "Missing module name"; { // Check if module is already loaded GSList *lmod = g_slist_find_custom(loaded_modules, arg, module_list_comparator); if (lmod) { loaded_module_t *module = lmod->data; if (manual) { if (!module->locked) { module->locked = TRUE; module->refcount += 1; return force ? NULL : "Module is already automatically loaded, " "marked as manually loaded"; } else return force ? NULL : "Module is already loaded"; } else { module->refcount += 1; return NULL; } } } { // Load module gchar *mdir = expand_filename(settings_opt_get("modules_dir")); gchar *path = g_module_build_path(mdir ? mdir : PKGLIB_DIR, arg); g_free(mdir); mod = g_module_open(path, G_MODULE_BIND_LAZY); g_free(path); if (!mod) return g_module_error(); } { // Obtain module information structures list gchar *varname = g_strdup_printf("info_%s", arg); gpointer var = NULL; // convert to a valid symbol name g_strcanon(varname, "abcdefghijklmnopqrstuvwxyz0123456789", '_'); if (!g_module_symbol(mod, varname, &var)) { if (!force) { g_free(varname); if(!g_module_close(mod)) scr_LogPrint(LPRINT_LOGNORM, "Error closing module: %s.", g_module_error()); return "Module provides no information structure"; } scr_LogPrint(LPRINT_LOGNORM, "Forced to ignore error: " "Module provides no information structure."); } g_free(varname); info = var; } // Find appropriate info struct if (info) { while (info) { if (!info->branch || !*(info->branch)) { scr_LogPrint(LPRINT_DEBUG, "No branch name, " "skipping info chunk."); } else if (strcmp(info->branch, mcabber_branch)) { scr_LogPrint(LPRINT_DEBUG, "Unhandled branch %s, " "skipping info chunk.", info->branch); } else if (info->api > mcabber_api_version || info->api < MCABBER_API_MIN) { // XXX force? if(!g_module_close(mod)) scr_LogPrint(LPRINT_LOGNORM, "Error closing module: %s.", g_module_error()); return "Incompatible mcabber API version"; } else break; info = info->next; } if (!info) { // XXX force? if(!g_module_close(mod)) scr_LogPrint(LPRINT_LOGNORM, "Error closing module: %s.", g_module_error()); return "No supported mcabber branch description found"; } } // Load dependencies if (info && info->requires) { const gchar **dep; GSList *deps = NULL; for (dep = info->requires; *dep; ++dep) { const gchar *err = module_load(*dep, FALSE, FALSE); if (err) { GSList *mel; scr_LogPrint(LPRINT_LOGNORM, "Error loading dependency module %s: %s.", *dep, err); // Unload already loaded dependencies for (mel = deps; mel; mel = mel->next) { gchar *ldmname = mel->data; err = module_unload(ldmname, FALSE, FALSE); scr_LogPrint(LPRINT_LOGNORM, "Error unloading dependency module %s: %s.", ldmname, err); g_free(ldmname); } g_slist_free(deps); // Unload module if (!g_module_close(mod)) scr_LogPrint(LPRINT_LOGNORM, "Error unloading module %s: %s.", arg, g_module_error()); return "Dependency problems"; } deps = g_slist_append(deps, (gpointer) *dep); } g_slist_free(deps); } { // Register module loaded_module_t *module = g_new(loaded_module_t, 1); module->refcount = 1; module->locked = manual; module->name = g_strdup(arg); module->module = mod; module->info = info; loaded_modules = g_slist_prepend(loaded_modules, module); } // Run initialization routine if (info && info->init) info->init(); // XXX Run hk_loaded_module hook (and move this line there) scr_LogPrint(LPRINT_LOGNORM, "Loaded module %s.", arg); return NULL; } // module_unload(modulename, manual, force) // Unload specified module and any automatically loaded modules // that are no more required. const gchar *module_unload(const gchar *arg, gboolean manual, gboolean force) { GSList *lmod; loaded_module_t *module; module_info_t *info; if (!arg || !*arg) return "Missing module name"; lmod = g_slist_find_custom(loaded_modules, arg, module_list_comparator); if (!lmod) return "Module not found"; module = lmod->data; // Check if user can unload this module if (manual) { if (!module->locked) { if (force) scr_LogPrint(LPRINT_LOGNORM, "Forced to ignore error: " "Manually unloading automatically loaded module."); else return "Module is not loaded manually"; } module->locked = FALSE; } // Check refcount module->refcount -= 1; if (module->refcount > 0) { if (force) scr_LogPrint(LPRINT_LOGNORM, "Forced to ignore error: " "Refcount is not zero (%u).", module->refcount); else return manual ? "Module is required by some other modules" : NULL; } info = module->info; // Run uninitialization routine if (info && info->uninit) info->uninit(); // Unload dependencies if (info && info->requires) { const gchar **dep; for (dep = info->requires; *dep; ++dep) { const gchar *err = module_unload(*dep, FALSE, FALSE); if (err) // XXX scr_LogPrint(LPRINT_LOGNORM, "Error unloading automatically loaded module %s: %s.", *dep, err); } } // XXX Prevent uninitialization routine and dep unloading to be performed again module->info = NULL; // Unload module if (!g_module_close(module->module)) return g_module_error(); // XXX destroy structure? // Destroy structure loaded_modules = g_slist_delete_link(loaded_modules, lmod); // Output this here, as arg may point to module->name scr_LogPrint(LPRINT_LOGNORM, "Unloaded module %s.", module->name); g_free(module->name); g_free(module); return NULL; } // module_list_print(void) // Prints into status buffer and log list of the currently loaded // modules. void module_list_print(void) { GSList *mel; gsize maxlen = 0; gchar *format; GString *message; guint module_count = 0; if (!loaded_modules) { scr_LogPrint(LPRINT_LOGNORM, "No modules loaded."); return; } // Count maximum module name length for (mel = loaded_modules; mel; mel = mel -> next) { loaded_module_t *module = mel->data; gsize len = strlen(module->name); if (len > maxlen) maxlen = len; module_count++; } // Create format string format = g_strdup_printf("%%-%us %%2u (%%c)", (unsigned)maxlen); // Fill the message to be printed message = g_string_new("Loaded modules:\n"); for (mel = loaded_modules; mel; mel = mel -> next) { loaded_module_t *module = mel->data; g_string_append_printf(message, format, module->name, module->refcount, module->locked ? 'M' : 'A'); if (module->info) { module_info_t *info = module->info; // Module version if (info->version) { g_string_append(message, " version: "); g_string_append(message, info->version); } // Module dependencies if (info->requires && *(info->requires)) { const gchar **dep; g_string_append(message, " depends: "); for (dep = info->requires; *dep; ++dep) { g_string_append(message, *dep); g_string_append(message, ", "); } // Chop extra ", " g_string_truncate(message, message->len - 2); } } g_string_append_c(message, '\n'); } // Chop extra "\n" g_string_truncate(message, message->len - 1); scr_LogPrint(LPRINT_NORMAL, "%s", message->str); if (module_count + 1 > scr_getlogwinheight()) { scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE); scr_setattentionflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE, ROSTER_UI_PRIO_STATUS_WIN_MESSAGE, prio_max); } g_string_free(message, TRUE); g_free(format); } // module_info_print(name) // Prints info about specific module void module_info_print(const gchar *name) { GSList *lmod; loaded_module_t *module; module_info_t *info; if (!name || !name[0]) { scr_LogPrint(LPRINT_NORMAL, "Please specify a module name."); return; } lmod = g_slist_find_custom(loaded_modules, name, module_list_comparator); if (!lmod) { scr_LogPrint(LPRINT_NORMAL, "Module %s not found.", name); return; } module = lmod->data; info = module->info; scr_LogPrint(LPRINT_NORMAL, "Module %s", module->name); scr_LogPrint(LPRINT_NORMAL, " Location: %s", g_module_name(module->module)); scr_LogPrint(LPRINT_NORMAL, " Loaded: %s", module->locked ? "Manually" : "Automatically"); scr_LogPrint(LPRINT_NORMAL, " Reference count: %u", module->refcount); if (info) { if (info->version) scr_LogPrint(LPRINT_NORMAL, " Version: %s", info->version); scr_LogPrint(LPRINT_NORMAL, " API: %s:%u", info->branch, info->api); if (info->requires && *(info->requires)) { GString *message = g_string_new("Depends on: "); const gchar **dep; for (dep = info->requires; *dep; ++dep) { g_string_append(message, *dep); g_string_append(message, ", "); } // Chop last ", " g_string_truncate(message, message->len - 2); scr_LogPrint(LPRINT_NORMAL, " %s", message->str); g_string_free(message, TRUE); } if (info->description) scr_LogPrint(LPRINT_NORMAL, " Description: %s", info->description); } scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE); scr_setattentionflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE, ROSTER_UI_PRIO_STATUS_WIN_MESSAGE, prio_max); } // modules_init() // Initializes module system. void modules_init(void) { } // modules_deinit() // Unloads all the modules. void modules_deinit(void) { GSList *mel; // We need only manually loaded modules for (mel = loaded_modules; mel; mel = mel->next) { loaded_module_t *module = mel->data; if (module->locked) break; } while (mel) { loaded_module_t *module = mel->data; const gchar *err; // Find next manually loaded module to treat for (mel = mel->next; mel; mel = mel->next) { loaded_module_t *module = mel->data; if (module->locked) break; } // Unload module scr_LogPrint(LPRINT_LOGNORM, "Unloading module %s.", module->name); err = module_unload(module->name, TRUE, FALSE); if (err) scr_LogPrint(LPRINT_LOGNORM, "* Module unloading failed: %s.", err); } } /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2: For Vim users... */