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
--- 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);