Mercurial > ~mikael > mcabber > hg
comparison mcabber/mcabber/modules.c @ 1735:5093b5ca1572
New modules loading scheme
author | Myhailo Danylenko <isbear@ukrpost.net> |
---|---|
date | Thu, 04 Mar 2010 13:03:20 +0200 |
parents | |
children | 15e1f3957786 |
comparison
equal
deleted
inserted
replaced
1734:eae4a2637f2c | 1735:5093b5ca1572 |
---|---|
1 /* | |
2 * modules.c -- modules handling | |
3 * | |
4 * Copyright (C) 2010 Myhailo Danylenko <isbear@ukrpost.net> | |
5 * | |
6 * This program is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or (at | |
9 * your option) any later version. | |
10 * | |
11 * This program is distributed in the hope that it will be useful, but | |
12 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 * General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program; if not, write to the Free Software | |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
19 * USA | |
20 */ | |
21 | |
22 #include <glib.h> | |
23 #include <gmodule.h> | |
24 #include <string.h> | |
25 | |
26 #include "settings.h" | |
27 #include "config.h" | |
28 #include "modules.h" | |
29 #include "logprint.h" | |
30 #include "utils.h" | |
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 | |
43 // FIXME This should be a hash table | |
44 // but this needs long thinking and will not affect external interfaces | |
45 static GSList *loaded_modules = NULL; | |
46 | |
47 static gint module_list_comparator(gconstpointer arg1, gconstpointer arg2) | |
48 { | |
49 const loaded_module_t *module = arg1; | |
50 const char *name = arg2; | |
51 return g_strcmp0(module->name, name); | |
52 } | |
53 | |
54 // module_load(modulename, manual, force) | |
55 // Tries to load specified module and any modules, that this module | |
56 // depends on. Returns NULL on success or constant error string in a | |
57 // case of error. Error message not necessarily indicates error. | |
58 const gchar *module_load(const gchar *arg, gboolean manual, gboolean force) | |
59 { | |
60 GModule *mod; | |
61 module_info_t *info; | |
62 GSList *deps = NULL; | |
63 | |
64 if (!arg || !*arg) | |
65 return "Missing module name"; | |
66 | |
67 { // Check if module is already loaded | |
68 GSList *lmod = g_slist_find_custom(loaded_modules, arg, module_list_comparator); | |
69 | |
70 if (lmod) { | |
71 loaded_module_t *module = lmod->data; | |
72 | |
73 if (manual) { | |
74 if (!module->locked) { | |
75 module->locked = TRUE; | |
76 module->refcount += 1; | |
77 return force ? NULL : "Module is already automatically loaded, marked as manually loaded"; | |
78 } else | |
79 return force ? NULL : "Module is already loaded"; | |
80 } else { | |
81 module->refcount += 1; | |
82 return NULL; | |
83 } | |
84 } | |
85 } | |
86 | |
87 { // Load module | |
88 gchar *mdir = expand_filename(settings_opt_get("modules_dir")); | |
89 gchar *path = g_module_build_path(mdir ? mdir : PKGLIB_DIR, arg); | |
90 g_free(mdir); | |
91 mod = g_module_open(path, G_MODULE_BIND_LAZY); | |
92 g_free(path); | |
93 if (!mod) | |
94 return g_module_error(); | |
95 } | |
96 | |
97 { // Obtain module information structure | |
98 gchar *varname = g_strdup_printf("info_%s", arg); | |
99 gpointer var = NULL; | |
100 | |
101 // convert to a valid symbol name | |
102 g_strcanon(varname, "abcdefghijklmnopqrstuvwxyz0123456789", '_'); | |
103 | |
104 if (!g_module_symbol(mod, varname, &var)) { | |
105 if (!force) { | |
106 g_free(varname); | |
107 return "Module provides no information structure"; | |
108 } | |
109 | |
110 scr_LogPrint(LPRINT_LOGNORM, "Forced to ignore error: Module provides no information structure."); | |
111 } | |
112 | |
113 g_free(varname); | |
114 info = var; | |
115 } | |
116 | |
117 // Version check | |
118 if (info && info->mcabber_version && *(info->mcabber_version) | |
119 && (strcmp(info->mcabber_version, PACKAGE_VERSION) > 0)) { | |
120 if (!force) { | |
121 g_module_close(mod); | |
122 return "Module requires newer version of mcabber"; | |
123 } | |
124 | |
125 scr_LogPrint(LPRINT_LOGNORM, "Forced to ignore error: Module requires newer version of mcabber."); | |
126 } | |
127 | |
128 // Load dependencies | |
129 if (info && info->requires) { | |
130 const gchar **dep; | |
131 | |
132 for (dep = info->requires; *dep; ++dep) { | |
133 const gchar *err = module_load(*dep, FALSE, FALSE); | |
134 | |
135 if (err) { | |
136 GSList *mel; | |
137 scr_LogPrint(LPRINT_LOGNORM, "Error loading dependency module %s: %s.", *dep, err); | |
138 | |
139 // Unload already loaded dependencies | |
140 for (mel = deps; mel; mel = mel->next) { | |
141 gchar *ldmname = mel->data; | |
142 err = module_unload(ldmname, FALSE, FALSE); | |
143 scr_LogPrint(LPRINT_LOGNORM, "Error unloading dependency module %s: %s.", ldmname, err); | |
144 g_free(ldmname); | |
145 } | |
146 g_slist_free(deps); | |
147 | |
148 // Unload module | |
149 if (!g_module_close(mod)) | |
150 scr_LogPrint(LPRINT_LOGNORM, "Error unloading module %s: %s.", arg, g_module_error()); | |
151 return "Dependency problems"; | |
152 } | |
153 | |
154 deps = g_slist_append(deps, g_strdup(*dep)); | |
155 } | |
156 } | |
157 | |
158 { // Register module | |
159 loaded_module_t *module = g_new(loaded_module_t, 1); | |
160 | |
161 module->refcount = 1; | |
162 module->locked = manual; | |
163 module->name = g_strdup(arg); | |
164 module->module = mod; | |
165 module->info = info; | |
166 module->dependencies = deps; | |
167 | |
168 loaded_modules = g_slist_prepend(loaded_modules, module); | |
169 } | |
170 | |
171 // Run initialization routine | |
172 if (info && info->init) | |
173 info->init(); | |
174 | |
175 // XXX Run hk_loaded_module hook (and move this line there) | |
176 scr_LogPrint(LPRINT_LOGNORM, "Loaded module %s.", arg); | |
177 | |
178 return NULL; | |
179 } | |
180 | |
181 // module_unload(modulename, manual, force) | |
182 // Unload specified module and any automatically loaded modules | |
183 // that are no more required. | |
184 const gchar *module_unload(const gchar *arg, gboolean manual, gboolean force) | |
185 { | |
186 GSList *lmod; | |
187 loaded_module_t *module; | |
188 module_info_t *info; | |
189 | |
190 if (!arg || !*arg) | |
191 return "Missing module name"; | |
192 | |
193 lmod = g_slist_find_custom(loaded_modules, arg, module_list_comparator); | |
194 if (!lmod) | |
195 return "Module not found"; | |
196 | |
197 module = lmod->data; | |
198 | |
199 // Check if user can unload this module | |
200 if (manual) { | |
201 if (!module->locked) { | |
202 if (force) | |
203 scr_LogPrint(LPRINT_LOGNORM, "Forced to ignore error: Manually unloading automatically loaded module."); | |
204 else | |
205 return "Module is not loaded manually"; | |
206 } | |
207 module->locked = FALSE; | |
208 } | |
209 | |
210 // Check refcount | |
211 module->refcount -= 1; | |
212 if (module->refcount > 0) { | |
213 if (force) | |
214 scr_LogPrint(LPRINT_LOGNORM, "Forced to ignore error: Refcount is not zero (%u).", module->refcount); | |
215 else | |
216 return manual ? "Module is required by some other modules" : NULL; | |
217 } | |
218 | |
219 info = module->info; | |
220 | |
221 // Run uninitialization routine | |
222 if (info && info->uninit) | |
223 info->uninit(); | |
224 // XXX Prevent uninitialization routine to be called again | |
225 module->info = NULL; | |
226 | |
227 // Unload module | |
228 if (!g_module_close(module->module)) | |
229 return g_module_error(); // XXX destroy structure? | |
230 | |
231 { // Unload dependencies | |
232 GSList *dep; | |
233 for (dep = module->dependencies; dep; dep = dep->next) { | |
234 gchar *ldmname = dep->data; | |
235 const gchar *err = module_unload(ldmname, FALSE, FALSE); | |
236 if (err) // XXX | |
237 scr_LogPrint(LPRINT_LOGNORM, "Error unloading automatically loaded module %s: %s.", ldmname, err); | |
238 g_free(ldmname); | |
239 } | |
240 g_slist_free(module->dependencies); | |
241 module->dependencies = NULL; | |
242 } | |
243 | |
244 // Destroy structure | |
245 loaded_modules = g_slist_delete_link(loaded_modules, lmod); | |
246 g_free(module->name); | |
247 g_free(module); | |
248 | |
249 scr_LogPrint(LPRINT_LOGNORM, "Unloaded module %s.", arg); | |
250 | |
251 return NULL; | |
252 } | |
253 | |
254 // module_list_print(void) | |
255 // Prints into status buffer and log list of the currently loaded | |
256 // modules. | |
257 void module_list_print(void) | |
258 { | |
259 GSList *mel; | |
260 gsize maxlen = 0; | |
261 gchar *format; | |
262 GString *message; | |
263 | |
264 if (!loaded_modules) { | |
265 scr_LogPrint(LPRINT_LOGNORM, "No modules loaded."); | |
266 return; | |
267 } | |
268 | |
269 // Counnt maximum module name length | |
270 for (mel = loaded_modules; mel; mel = mel -> next) { | |
271 loaded_module_t *module = mel->data; | |
272 gsize len = strlen(module->name); | |
273 if (len > maxlen) | |
274 maxlen = len; | |
275 } | |
276 | |
277 // Create format string | |
278 format = g_strdup_printf("%%-%us %%2u (%%c)", maxlen); | |
279 | |
280 // Fill the message to be printed | |
281 message = g_string_new("Loaded modules:\n"); | |
282 for (mel = loaded_modules; mel; mel = mel -> next) { | |
283 loaded_module_t *module = mel->data; | |
284 GSList *dep; | |
285 | |
286 g_string_append_printf(message, format, module->name, module->refcount, module->locked ? 'M' : 'A'); | |
287 | |
288 // Append loaded module dependencies | |
289 if (module->dependencies) { | |
290 g_string_append(message, " depends: "); | |
291 | |
292 for (dep = module->dependencies; dep; dep = dep->next) { | |
293 const gchar *name = dep->data; | |
294 g_string_append(message, name); | |
295 g_string_append(message, ", "); | |
296 } | |
297 | |
298 // Chop extra ", " | |
299 g_string_truncate(message, message->len - 2); | |
300 } | |
301 | |
302 g_string_append_c(message, '\n'); | |
303 } | |
304 | |
305 // Chop extra "\n" | |
306 g_string_truncate(message, message->len - 1); | |
307 | |
308 scr_LogPrint(LPRINT_LOGNORM, "%s", message->str); | |
309 | |
310 g_string_free(message, TRUE); | |
311 g_free(format); | |
312 } | |
313 | |
314 // modules_init() | |
315 // Initializes module system. | |
316 void modules_init(void) | |
317 { | |
318 } | |
319 | |
320 // modules_deinit() | |
321 // Unloads all the modules. | |
322 void modules_deinit(void) | |
323 { | |
324 GSList *mel; | |
325 | |
326 // We need only manually loaded modules | |
327 for (mel = loaded_modules; mel; mel = mel->next) { | |
328 loaded_module_t *module = mel->data; | |
329 if (module->locked) | |
330 break; | |
331 } | |
332 | |
333 while (mel) { | |
334 loaded_module_t *module = mel->data; | |
335 const gchar *err; | |
336 | |
337 // Find next manually loaded module to treat | |
338 for (mel = mel->next; mel; mel = mel->next) { | |
339 loaded_module_t *module = mel->data; | |
340 if (module->locked) | |
341 break; | |
342 } | |
343 | |
344 // Unload module | |
345 scr_LogPrint(LPRINT_LOGNORM, "Unloading module %s.", module->name); | |
346 err = module_unload(module->name, TRUE, FALSE); | |
347 if (err) | |
348 scr_LogPrint(LPRINT_LOGNORM, "* Module unloading failed: %s.", err); | |
349 } | |
350 } | |
351 | |
352 /* vim: set expandtab cindent cinoptions=>2\:2(0 ts=2 sw=2: For Vim users... */ |