diff mcabber/mcabber/modules.c @ 1749:7ee390513463

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
author Myhailo Danylenko <isbear@ukrpost.net>
date Sat, 13 Mar 2010 10:29:18 +0100
parents 15e1f3957786
children 84487d78d0ea
line wrap: on
line diff
--- 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)