Mercurial > ~mikael > mcabber > hg
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) |