# HG changeset patch # User Myhailo Danylenko # Date 1268472558 -3600 # Node ID 7ee390513463761570b522223c3bd48e1263aa19 # Parent 51a23403cc80a0037b35143957ff92a4071e6c93 Use api version for module checks * Change module structures * Check for supported api versions at loading time * Add info command, description and module version fields diff -r 51a23403cc80 -r 7ee390513463 mcabber/configure.ac --- a/mcabber/configure.ac Fri Mar 12 19:13:56 2010 +0100 +++ b/mcabber/configure.ac Sat Mar 13 10:29:18 2010 +0100 @@ -257,6 +257,12 @@ AM_CONDITIONAL([OTR], [test x$libotr_found = xyes]) AM_CONDITIONAL([INSTALL_HEADERS], [test x$enable_modules = xyes]) +# Prepare some config.h variables +AC_DEFINE([MCABBER_BRANCH], "dev", [Mcabber branch]) +AC_DEFINE([MCABBER_API_VERSION], 1, [Mcabber API version]) +AC_DEFINE([MCABBER_API_MIN], 1, [Minimum supported mcabber API version]) +AC_DEFINE([MCABBER_VERSION], "AC_PACKAGE_VERSION", [Mcabber version string]) + # We need _GNU_SOURCE for strptime() and strcasestr() CFLAGS="$CFLAGS -D_GNU_SOURCE" diff -r 51a23403cc80 -r 7ee390513463 mcabber/include/config.h.in --- a/mcabber/include/config.h.in Fri Mar 12 19:13:56 2010 +0100 +++ b/mcabber/include/config.h.in Sat Mar 13 10:29:18 2010 +0100 @@ -1,7 +1,7 @@ #ifndef __MCABBER_CONFIG_H__ #define __MCABBER_CONFIG_H__ 1 -/* ... */ +/* Are modules enabled or no */ #undef MODULES_ENABLE /* ... */ @@ -43,13 +43,25 @@ /* ... */ #undef HAVE_STRCASESTR -/* ... */ +/* Default data files prefix */ #undef DATA_DIR -/* ... */ +/* Default modules location */ #undef PKGLIB_DIR -/* ... */ +/* To satisfy gnutls */ #undef _FILE_OFFSET_BITS +/* Mcabber branch name (string) */ +#undef MCABBER_BRANCH + +/* Api version of mcabber branch */ +#undef MCABBER_API_VERSION + +/* XXX */ +#undef MCABBER_API_MIN + +/* Mcabber version (string) */ +#undef MCABBER_VERSION + #endif diff -r 51a23403cc80 -r 7ee390513463 mcabber/mcabber/commands.c --- a/mcabber/mcabber/commands.c Fri Mar 12 19:13:56 2010 +0100 +++ b/mcabber/mcabber/commands.c Sat Mar 13 10:29:18 2010 +0100 @@ -494,7 +494,7 @@ } if (current_buddy) { if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP) - do_group("toggle"); + do_group("toggle"); else { // Enter chat mode scr_set_chatmode(TRUE); @@ -2967,7 +2967,7 @@ if (!args[0] || !strcmp(args[0], "list")) { module_list_print(); } else { - const gchar *error; + const gchar *error = NULL; const gchar *name = args[1]; if (name && name[0] == '-' && name[1] == 'f') { @@ -2981,6 +2981,8 @@ error = module_load(name, TRUE, force); else if (!strcmp(args[0], "unload")) error = module_unload(name, TRUE, force); + else if (!strcmp(args[0], "info")) + module_info_print(name); else error = "Unknown subcommand"; if (error) diff -r 51a23403cc80 -r 7ee390513463 mcabber/mcabber/modules.c --- a/mcabber/mcabber/modules.c Fri Mar 12 19:13:56 2010 +0100 +++ b/mcabber/mcabber/modules.c Sat Mar 13 10:29:18 2010 +0100 @@ -29,20 +29,11 @@ #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 +GSList *loaded_modules = NULL; -// 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; +const gchar *mcabber_branch = MCABBER_BRANCH; +const guint mcabber_api_version = MCABBER_API_VERSION; static gint module_list_comparator(gconstpointer arg1, gconstpointer arg2) { @@ -59,7 +50,6 @@ { GModule *mod; module_info_t *info; - GSList *deps = NULL; if (!arg || !*arg) return "Missing module name"; @@ -96,7 +86,7 @@ return g_module_error(); } - { // Obtain module information structure + { // Obtain module information structures list gchar *varname = g_strdup_printf("info_%s", arg); gpointer var = NULL; @@ -106,6 +96,9 @@ 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"; } @@ -117,21 +110,38 @@ 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"; + // 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; } - scr_LogPrint(LPRINT_LOGNORM, "Forced to ignore error: " - "Module requires newer version of mcabber."); + 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); @@ -159,8 +169,10 @@ return "Dependency problems"; } - deps = g_slist_append(deps, g_strdup(*dep)); + deps = g_slist_append(deps, (gpointer) *dep); } + + g_slist_free(deps); } { // Register module @@ -171,7 +183,6 @@ module->name = g_strdup(arg); module->module = mod; module->info = info; - module->dependencies = deps; loaded_modules = g_slist_prepend(loaded_modules, module); } @@ -231,28 +242,26 @@ // Run uninitialization routine if (info && info->uninit) info->uninit(); - // XXX Prevent uninitialization routine to be called again + + // 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? - { // 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); @@ -278,7 +287,7 @@ return; } - // Counnt maximum module name length + // Count maximum module name length for (mel = loaded_modules; mel; mel = mel -> next) { loaded_module_t *module = mel->data; gsize len = strlen(module->name); @@ -293,23 +302,32 @@ 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: "); + if (module->info) { + module_info_t *info = module->info; - for (dep = module->dependencies; dep; dep = dep->next) { - const gchar *name = dep->data; - g_string_append(message, name); - g_string_append(message, ", "); + // Module version + if (info->version) { + g_string_append(message, " version: "); + g_string_append(message, info->version); } - // Chop extra ", " - g_string_truncate(message, message->len - 2); + // 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'); @@ -324,6 +342,53 @@ 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; + + 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, "Name: %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); + + 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); + } +} + // modules_init() // Initializes module system. void modules_init(void) diff -r 51a23403cc80 -r 7ee390513463 mcabber/mcabber/modules.h --- a/mcabber/mcabber/modules.h Fri Mar 12 19:13:56 2010 +0100 +++ b/mcabber/mcabber/modules.h Sat Mar 13 10:29:18 2010 +0100 @@ -2,6 +2,8 @@ #define __MCABBER_MODULES_H__ 1 #include +#include +#include // MCABBER_BRANCH, MCABBER_API_VERSION // Module loading process looks like this: // check, if module is loaded @@ -20,18 +22,42 @@ typedef void (*module_init_t)(void); typedef void (*module_uninit_t)(void); -// public module-describing structure -typedef struct { - const gchar *mcabber_version; // Contains mcabber version string, that this module is written to work with +// Structure, that module should provide +typedef struct module_info_struct module_info_t; +struct module_info_struct { + const gchar *branch; // Contains mcabber branch name, that this module is written to work with module_init_t init; // Initialization callback to be called after all dependencies will be loaded module_uninit_t uninit; // Uninitialization callback to be called before module unloading const gchar **requires; // NULL-terminated list of module names, that must be loaded before this module -} module_info_t; + guint api; // Mcabber branch api version, that module is supposed to work with + const gchar *version; // Module version string. Optional. + const gchar *description; // Module description. Can contain multiple lines. + module_info_t *next; // If module supports multiple branches, it can provide several branch structs. +}; const gchar *module_load(const gchar *name, gboolean manual, gboolean force); const gchar *module_unload(const gchar *name, gboolean manual, gboolean force); +// Grey zone (these symbols are semi-private and are exposed only for compatibility modules) + +// Information about loaded module +typedef struct { + guint refcount; // Reference count + gboolean locked; // If true, one of references is manual + gchar *name; // Module name + GModule *module; // Module object + module_info_t *info; // Module information struct. May be NULL! +} loaded_module_t; + +// Registry of loaded modules +extern GSList *loaded_modules; +extern const gchar *mcabber_branch; +extern const guint mcabber_api_version; + +// Should be considered mcabber private and not a part of api + void module_list_print(void); +void module_info_print(const gchar *name); void modules_init(void); void modules_deinit(void);