Mercurial > ~mikael > mcabber > hg
view mcabber/mcabber/modules.c @ 1746:b7574de9ae4b
Use random() instead of rand()
(As recommended in the manpage...)
author | Mikael Berthe <mikael@lilotux.net> |
---|---|
date | Thu, 11 Mar 2010 21:53:46 +0100 |
parents | 15e1f3957786 |
children | 7ee390513463 |
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 "logprint.h" #include "utils.h" // Information about loaded module typedef struct { guint refcount; gboolean locked; gchar *name; GModule *module; GSList *dependencies; module_info_t *info; } loaded_module_t; // Registry of loaded modules // FIXME This should be a hash table // but this needs long thinking and will not affect external interfaces static GSList *loaded_modules = NULL; 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; GSList *deps = NULL; 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 structure 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); return "Module provides no information structure"; } scr_LogPrint(LPRINT_LOGNORM, "Forced to ignore error: " "Module provides no information structure."); } g_free(varname); info = var; } // Version check if (info && info->mcabber_version && *(info->mcabber_version) && (strcmp(info->mcabber_version, PACKAGE_VERSION) > 0)) { if (!force) { g_module_close(mod); return "Module requires newer version of mcabber"; } scr_LogPrint(LPRINT_LOGNORM, "Forced to ignore error: " "Module requires newer version of mcabber."); } // Load dependencies if (info && info->requires) { const gchar **dep; 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, g_strdup(*dep)); } } { // 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; module->dependencies = deps; 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(); // XXX Prevent uninitialization routine to be called again module->info = NULL; // Unload module if (!g_module_close(module->module)) return g_module_error(); // XXX destroy structure? { // Unload dependencies GSList *dep; for (dep = module->dependencies; dep; dep = dep->next) { gchar *ldmname = dep->data; const gchar *err = module_unload(ldmname, FALSE, FALSE); if (err) // XXX scr_LogPrint(LPRINT_LOGNORM, "Error unloading automatically loaded module %s: %s.", ldmname, err); g_free(ldmname); } g_slist_free(module->dependencies); module->dependencies = NULL; } // Destroy structure loaded_modules = g_slist_delete_link(loaded_modules, lmod); g_free(module->name); g_free(module); scr_LogPrint(LPRINT_LOGNORM, "Unloaded module %s.", arg); 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; if (!loaded_modules) { scr_LogPrint(LPRINT_LOGNORM, "No modules loaded."); return; } // Counnt 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; } // Create format string format = g_strdup_printf("%%-%us %%2u (%%c)", 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; GSList *dep; g_string_append_printf(message, format, module->name, module->refcount, module->locked ? 'M' : 'A'); // Append loaded module dependencies if (module->dependencies) { g_string_append(message, " depends: "); for (dep = module->dependencies; dep; dep = dep->next) { const gchar *name = dep->data; g_string_append(message, name); 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_LOGNORM, "%s", message->str); g_string_free(message, TRUE); g_free(format); } // 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 et cindent cinoptions=>2\:2(0 ts=2 sw=2: For Vim users... */