comparison 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
comparison
equal deleted inserted replaced
1748:51a23403cc80 1749:7ee390513463
27 #include "config.h" 27 #include "config.h"
28 #include "modules.h" 28 #include "modules.h"
29 #include "logprint.h" 29 #include "logprint.h"
30 #include "utils.h" 30 #include "utils.h"
31 31
32 // Information about loaded module
33 typedef struct {
34 guint refcount;
35 gboolean locked;
36 gchar *name;
37 GModule *module;
38 GSList *dependencies;
39 module_info_t *info;
40 } loaded_module_t;
41
42 // Registry of loaded modules 32 // Registry of loaded modules
43 // FIXME This should be a hash table 33 GSList *loaded_modules = NULL;
44 // but this needs long thinking and will not affect external interfaces 34
45 static GSList *loaded_modules = NULL; 35 const gchar *mcabber_branch = MCABBER_BRANCH;
36 const guint mcabber_api_version = MCABBER_API_VERSION;
46 37
47 static gint module_list_comparator(gconstpointer arg1, gconstpointer arg2) 38 static gint module_list_comparator(gconstpointer arg1, gconstpointer arg2)
48 { 39 {
49 const loaded_module_t *module = arg1; 40 const loaded_module_t *module = arg1;
50 const char *name = arg2; 41 const char *name = arg2;
57 // case of error. Error message not necessarily indicates error. 48 // case of error. Error message not necessarily indicates error.
58 const gchar *module_load(const gchar *arg, gboolean manual, gboolean force) 49 const gchar *module_load(const gchar *arg, gboolean manual, gboolean force)
59 { 50 {
60 GModule *mod; 51 GModule *mod;
61 module_info_t *info; 52 module_info_t *info;
62 GSList *deps = NULL;
63 53
64 if (!arg || !*arg) 54 if (!arg || !*arg)
65 return "Missing module name"; 55 return "Missing module name";
66 56
67 { // Check if module is already loaded 57 { // Check if module is already loaded
94 g_free(path); 84 g_free(path);
95 if (!mod) 85 if (!mod)
96 return g_module_error(); 86 return g_module_error();
97 } 87 }
98 88
99 { // Obtain module information structure 89 { // Obtain module information structures list
100 gchar *varname = g_strdup_printf("info_%s", arg); 90 gchar *varname = g_strdup_printf("info_%s", arg);
101 gpointer var = NULL; 91 gpointer var = NULL;
102 92
103 // convert to a valid symbol name 93 // convert to a valid symbol name
104 g_strcanon(varname, "abcdefghijklmnopqrstuvwxyz0123456789", '_'); 94 g_strcanon(varname, "abcdefghijklmnopqrstuvwxyz0123456789", '_');
105 95
106 if (!g_module_symbol(mod, varname, &var)) { 96 if (!g_module_symbol(mod, varname, &var)) {
107 if (!force) { 97 if (!force) {
108 g_free(varname); 98 g_free(varname);
99 if(!g_module_close(mod))
100 scr_LogPrint(LPRINT_LOGNORM, "Error closing module: %s.",
101 g_module_error());
109 return "Module provides no information structure"; 102 return "Module provides no information structure";
110 } 103 }
111 104
112 scr_LogPrint(LPRINT_LOGNORM, "Forced to ignore error: " 105 scr_LogPrint(LPRINT_LOGNORM, "Forced to ignore error: "
113 "Module provides no information structure."); 106 "Module provides no information structure.");
115 108
116 g_free(varname); 109 g_free(varname);
117 info = var; 110 info = var;
118 } 111 }
119 112
120 // Version check 113 // Find appropriate info struct
121 if (info && info->mcabber_version && *(info->mcabber_version) 114 if (info) {
122 && (strcmp(info->mcabber_version, PACKAGE_VERSION) > 0)) { 115 while (info) {
123 if (!force) { 116 if (!info->branch || !*(info->branch)) {
124 g_module_close(mod); 117 scr_LogPrint(LPRINT_DEBUG, "No branch name, "
125 return "Module requires newer version of mcabber"; 118 "skipping info chunk.");
126 } 119 } else if (strcmp(info->branch, mcabber_branch)) {
127 120 scr_LogPrint(LPRINT_DEBUG, "Unhandled branch %s, "
128 scr_LogPrint(LPRINT_LOGNORM, "Forced to ignore error: " 121 "skipping info chunk.", info->branch);
129 "Module requires newer version of mcabber."); 122 } else if (info->api > mcabber_api_version ||
123 info->api < MCABBER_API_MIN) { // XXX force?
124 if(!g_module_close(mod))
125 scr_LogPrint(LPRINT_LOGNORM, "Error closing module: %s.",
126 g_module_error());
127 return "Incompatible mcabber api version";
128 } else
129 break;
130 info = info->next;
131 }
132
133 if (!info) { // XXX force?
134 if(!g_module_close(mod))
135 scr_LogPrint(LPRINT_LOGNORM, "Error closing module: %s.",
136 g_module_error());
137 return "No supported mcabber branch description found";
138 }
130 } 139 }
131 140
132 // Load dependencies 141 // Load dependencies
133 if (info && info->requires) { 142 if (info && info->requires) {
134 const gchar **dep; 143 const gchar **dep;
144 GSList *deps = NULL;
135 145
136 for (dep = info->requires; *dep; ++dep) { 146 for (dep = info->requires; *dep; ++dep) {
137 const gchar *err = module_load(*dep, FALSE, FALSE); 147 const gchar *err = module_load(*dep, FALSE, FALSE);
138 148
139 if (err) { 149 if (err) {
157 scr_LogPrint(LPRINT_LOGNORM, "Error unloading module %s: %s.", 167 scr_LogPrint(LPRINT_LOGNORM, "Error unloading module %s: %s.",
158 arg, g_module_error()); 168 arg, g_module_error());
159 return "Dependency problems"; 169 return "Dependency problems";
160 } 170 }
161 171
162 deps = g_slist_append(deps, g_strdup(*dep)); 172 deps = g_slist_append(deps, (gpointer) *dep);
163 } 173 }
174
175 g_slist_free(deps);
164 } 176 }
165 177
166 { // Register module 178 { // Register module
167 loaded_module_t *module = g_new(loaded_module_t, 1); 179 loaded_module_t *module = g_new(loaded_module_t, 1);
168 180
169 module->refcount = 1; 181 module->refcount = 1;
170 module->locked = manual; 182 module->locked = manual;
171 module->name = g_strdup(arg); 183 module->name = g_strdup(arg);
172 module->module = mod; 184 module->module = mod;
173 module->info = info; 185 module->info = info;
174 module->dependencies = deps;
175 186
176 loaded_modules = g_slist_prepend(loaded_modules, module); 187 loaded_modules = g_slist_prepend(loaded_modules, module);
177 } 188 }
178 189
179 // Run initialization routine 190 // Run initialization routine
229 info = module->info; 240 info = module->info;
230 241
231 // Run uninitialization routine 242 // Run uninitialization routine
232 if (info && info->uninit) 243 if (info && info->uninit)
233 info->uninit(); 244 info->uninit();
234 // XXX Prevent uninitialization routine to be called again 245
246 // Unload dependencies
247 if (info && info->requires) {
248 const gchar **dep;
249 for (dep = info->requires; *dep; ++dep) {
250 const gchar *err = module_unload(*dep, FALSE, FALSE);
251 if (err) // XXX
252 scr_LogPrint(LPRINT_LOGNORM,
253 "Error unloading automatically loaded module %s: %s.",
254 *dep, err);
255 }
256 }
257
258 // XXX Prevent uninitialization routine and dep unloading to be performed again
235 module->info = NULL; 259 module->info = NULL;
236 260
237 // Unload module 261 // Unload module
238 if (!g_module_close(module->module)) 262 if (!g_module_close(module->module))
239 return g_module_error(); // XXX destroy structure? 263 return g_module_error(); // XXX destroy structure?
240
241 { // Unload dependencies
242 GSList *dep;
243 for (dep = module->dependencies; dep; dep = dep->next) {
244 gchar *ldmname = dep->data;
245 const gchar *err = module_unload(ldmname, FALSE, FALSE);
246 if (err) // XXX
247 scr_LogPrint(LPRINT_LOGNORM,
248 "Error unloading automatically loaded module %s: %s.",
249 ldmname, err);
250 g_free(ldmname);
251 }
252 g_slist_free(module->dependencies);
253 module->dependencies = NULL;
254 }
255 264
256 // Destroy structure 265 // Destroy structure
257 loaded_modules = g_slist_delete_link(loaded_modules, lmod); 266 loaded_modules = g_slist_delete_link(loaded_modules, lmod);
258 g_free(module->name); 267 g_free(module->name);
259 g_free(module); 268 g_free(module);
276 if (!loaded_modules) { 285 if (!loaded_modules) {
277 scr_LogPrint(LPRINT_LOGNORM, "No modules loaded."); 286 scr_LogPrint(LPRINT_LOGNORM, "No modules loaded.");
278 return; 287 return;
279 } 288 }
280 289
281 // Counnt maximum module name length 290 // Count maximum module name length
282 for (mel = loaded_modules; mel; mel = mel -> next) { 291 for (mel = loaded_modules; mel; mel = mel -> next) {
283 loaded_module_t *module = mel->data; 292 loaded_module_t *module = mel->data;
284 gsize len = strlen(module->name); 293 gsize len = strlen(module->name);
285 if (len > maxlen) 294 if (len > maxlen)
286 maxlen = len; 295 maxlen = len;
291 300
292 // Fill the message to be printed 301 // Fill the message to be printed
293 message = g_string_new("Loaded modules:\n"); 302 message = g_string_new("Loaded modules:\n");
294 for (mel = loaded_modules; mel; mel = mel -> next) { 303 for (mel = loaded_modules; mel; mel = mel -> next) {
295 loaded_module_t *module = mel->data; 304 loaded_module_t *module = mel->data;
296 GSList *dep;
297 305
298 g_string_append_printf(message, format, module->name, module->refcount, 306 g_string_append_printf(message, format, module->name, module->refcount,
299 module->locked ? 'M' : 'A'); 307 module->locked ? 'M' : 'A');
300 308
301 // Append loaded module dependencies 309 if (module->info) {
302 if (module->dependencies) { 310 module_info_t *info = module->info;
303 g_string_append(message, " depends: "); 311
304 312 // Module version
305 for (dep = module->dependencies; dep; dep = dep->next) { 313 if (info->version) {
306 const gchar *name = dep->data; 314 g_string_append(message, " version: ");
307 g_string_append(message, name); 315 g_string_append(message, info->version);
308 g_string_append(message, ", "); 316 }
309 } 317
310 318 // Module dependencies
311 // Chop extra ", " 319 if (info->requires && *(info->requires)) {
312 g_string_truncate(message, message->len - 2); 320 const gchar **dep;
321 g_string_append(message, " depends: ");
322
323 for (dep = info->requires; *dep; ++dep) {
324 g_string_append(message, *dep);
325 g_string_append(message, ", ");
326 }
327
328 // Chop extra ", "
329 g_string_truncate(message, message->len - 2);
330 }
313 } 331 }
314 332
315 g_string_append_c(message, '\n'); 333 g_string_append_c(message, '\n');
316 } 334 }
317 335
320 338
321 scr_LogPrint(LPRINT_LOGNORM, "%s", message->str); 339 scr_LogPrint(LPRINT_LOGNORM, "%s", message->str);
322 340
323 g_string_free(message, TRUE); 341 g_string_free(message, TRUE);
324 g_free(format); 342 g_free(format);
343 }
344
345 // module_info_print(name)
346 // Prints info about specific module
347 void module_info_print(const gchar *name)
348 {
349 GSList *lmod;
350 loaded_module_t *module;
351 module_info_t *info;
352
353 lmod = g_slist_find_custom(loaded_modules, name, module_list_comparator);
354 if (!lmod) {
355 scr_LogPrint(LPRINT_NORMAL, "Module %s not found.", name);
356 return;
357 }
358
359 module = lmod->data;
360 info = module->info;
361
362 scr_LogPrint(LPRINT_NORMAL, "Name: %s", module->name);
363 scr_LogPrint(LPRINT_NORMAL, "Location: %s", g_module_name(module->module));
364 scr_LogPrint(LPRINT_NORMAL, "Loaded: %s",
365 module->locked ? "Manually" : "Automatically");
366 scr_LogPrint(LPRINT_NORMAL, "Reference count: %u", module->refcount);
367
368 if (info) {
369
370 if (info->version)
371 scr_LogPrint(LPRINT_NORMAL, "Version: %s", info->version);
372
373 if (info->requires && *(info->requires)) {
374 GString *message = g_string_new("Depends on: ");
375 const gchar **dep;
376 for (dep = info->requires; *dep; ++dep) {
377 g_string_append(message, *dep);
378 g_string_append(message, ", ");
379 }
380
381 // Chop last ", "
382 g_string_truncate(message, message->len - 2);
383 scr_LogPrint(LPRINT_NORMAL, "%s", message->str);
384 g_string_free(message, TRUE);
385 }
386
387 if (info->description)
388 scr_LogPrint(LPRINT_NORMAL, "Description: %s", info->description);
389 }
325 } 390 }
326 391
327 // modules_init() 392 // modules_init()
328 // Initializes module system. 393 // Initializes module system.
329 void modules_init(void) 394 void modules_init(void)