changeset 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 51a23403cc80
children 14b4866cc9f2
files mcabber/configure.ac mcabber/include/config.h.in mcabber/mcabber/commands.c mcabber/mcabber/modules.c mcabber/mcabber/modules.h
diffstat 5 files changed, 173 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- 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"
 
--- 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
--- 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)
--- 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)
--- 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 <glib.h>
+#include <gmodule.h>
+#include <mcabber/config.h> // 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);