eccb5352 |
1 | |
2 | /* module.c : Implementation of the module loading/unloading mechanism. |
3 | * |
4 | */ |
5 | |
6 | /* Initial draft by Michel Dagenais May 2003 |
7 | * Reworked by Mathieu Desnoyers, May 2003 |
8 | */ |
9 | |
eccb5352 |
10 | #include <popt.h> |
3d218f2a |
11 | #include <lttv/module.h> |
fcdf0ec2 |
12 | #include <lttv/lttv.h> |
eccb5352 |
13 | |
14 | /* Table of loaded modules and paths where to search for modules */ |
15 | |
16 | static GHashTable *modules = NULL; |
17 | |
18 | static GPtrArray *modulesStandalone = NULL; |
19 | |
20 | static GPtrArray *modulesPaths = NULL; |
21 | |
22 | void lttv_module_init(int argc, char **argv) { |
23 | modules = g_hash_table_new(g_str_hash, g_str_equal); |
24 | modulesStandalone = g_ptr_array_new(); |
25 | modulesPaths = g_ptr_array_new(); |
26 | } |
27 | |
28 | void lttv_module_destroy() { |
29 | |
30 | int i; |
31 | |
32 | /* Unload all modules */ |
33 | lttv_module_unload_all(); |
34 | |
35 | /* Free the modules paths pointer array as well as the elements */ |
36 | for(i = 0; i< modulesPaths->len; i++) { |
37 | g_free(modulesPaths->pdata[i]); |
38 | } |
39 | g_ptr_array_free(modulesPaths,TRUE) ; |
40 | g_ptr_array_free(modulesStandalone,TRUE) ; |
41 | modulesPaths = NULL; |
42 | modulesStandalone = NULL; |
43 | |
44 | /* destroy the hash table */ |
45 | g_hash_table_destroy(modules) ; |
46 | modules = NULL; |
47 | } |
48 | |
49 | /* Add a new pathname to the modules loading search path */ |
50 | |
51 | void lttv_module_path_add(const char *name) { |
52 | g_ptr_array_add(modulesPaths,(char*)g_strdup(name)); |
53 | } |
54 | |
55 | |
56 | /* Load (if not already loaded) the named module. Its init function is |
57 | called. We pass the options of the command line to it in case it has |
58 | preliminary things to get from it. Note that the normal way to add a |
59 | command line option for a module is through the options parsing mecanism. |
60 | */ |
61 | |
62 | lttv_module_info *lttv_module_load(const char *name, int argc, char **argv, loadtype load) { |
63 | |
64 | GModule *gmodule; |
65 | |
66 | lttv_module_info *moduleInfo; |
67 | |
68 | int i; |
69 | |
70 | char *pathname; |
71 | |
72 | lttv_module_load_init init_Function; |
73 | |
74 | /* Find and load the module, It will increase the usage counter |
75 | * If the module is already loaded, only the reference counter will |
76 | * be incremented. It's part of the gmodule architecture. Very useful |
77 | * for modules dependencies. |
78 | */ |
79 | |
80 | g_assert(name != NULL); |
81 | |
82 | for(i = 0 ; i < modulesPaths->len ; i++) { |
83 | pathname = g_module_build_path(modulesPaths->pdata[i],name); |
84 | gmodule = g_module_open(pathname,0) ; |
85 | |
86 | |
87 | if(gmodule != NULL) { |
88 | g_message("Loading module %s ... found!",pathname); |
89 | |
90 | /* Was the module already opened? */ |
91 | moduleInfo = g_hash_table_lookup(modules,g_module_name(gmodule)); |
92 | |
93 | /* First time the module is opened */ |
94 | |
95 | if(moduleInfo == NULL ) { |
96 | moduleInfo = g_new(lttv_module_info, 1); |
97 | moduleInfo->module = gmodule; |
98 | moduleInfo->pathname = g_module_name(gmodule); |
99 | moduleInfo->directory = modulesPaths->pdata[i]; |
100 | moduleInfo->name = (char *)g_strdup(name); |
101 | moduleInfo->ref_count = 0; |
102 | moduleInfo->index_standalone = -1; |
103 | g_hash_table_insert(modules, moduleInfo->pathname, moduleInfo); |
104 | if(!g_module_symbol(gmodule, "init", (gpointer) &init_Function)) { |
105 | g_critical("module %s (%s) does not have init function", |
106 | moduleInfo->pathname,moduleInfo->name); |
107 | } |
108 | else { |
109 | init_Function(argc,argv); |
110 | } |
111 | } |
112 | |
113 | /* Add the module in the standalone array if the module is |
114 | * standalone and not in the array. Otherwise, set index to |
115 | * -1 (dependant only). |
116 | */ |
117 | if(load == STANDALONE) { |
118 | |
119 | if(moduleInfo->index_standalone == -1) { |
120 | |
121 | g_ptr_array_add(modulesStandalone, moduleInfo); |
122 | moduleInfo->index_standalone = modulesStandalone->len - 1; |
123 | |
124 | moduleInfo->ref_count++ ; |
125 | } |
126 | else { |
127 | g_warning("Module %s is already loaded standalone.",pathname); |
128 | /* Decrease the gmodule use_count. Has previously been increased in the g_module_open. */ |
129 | g_module_close(moduleInfo->module) ; |
130 | } |
131 | } |
132 | else { /* DEPENDANT */ |
133 | moduleInfo->ref_count++ ; |
134 | } |
135 | |
136 | return moduleInfo; |
137 | } |
138 | g_message("Loading module %s ... missing.",pathname); |
139 | g_free(pathname); |
140 | } |
141 | g_critical("module %s not found",name); |
142 | return NULL; |
143 | } |
144 | |
145 | /* Unload the named module. */ |
146 | |
147 | int lttv_module_unload_pathname(const char *pathname, loadtype load) { |
148 | |
149 | lttv_module_info *moduleInfo; |
150 | |
151 | moduleInfo = g_hash_table_lookup(modules, pathname); |
152 | |
153 | /* If no module of that name is loaded, nothing to unload. */ |
154 | if(moduleInfo != NULL) { |
155 | g_message("Unloading module %s : is loaded.\n", pathname) ; |
156 | lttv_module_unload(moduleInfo, load) ; |
157 | return 1; |
158 | } |
159 | else { |
160 | g_message("Unloading module %s : is not loaded.\n", pathname) ; |
161 | return 0; |
162 | } |
163 | |
164 | } |
165 | |
166 | int lttv_module_unload_name(const char *name, loadtype load) { |
167 | |
168 | int i; |
169 | |
170 | char *pathname; |
171 | |
172 | /* Find and load the module, It will increase the usage counter |
173 | * If the module is already loaded, only the reference counter will |
174 | * be incremented. It's part of the gmodule architecture. Very useful |
175 | * for modules dependencies. |
176 | */ |
177 | |
178 | g_assert(name != NULL); |
179 | |
180 | for(i = 0 ; i < modulesPaths->len ; i++) { |
181 | |
182 | pathname = g_module_build_path(modulesPaths->pdata[i],name); |
183 | |
184 | if(lttv_module_unload_pathname(pathname, load) == TRUE) |
185 | return TRUE ; |
186 | } |
187 | g_critical("module %s not found",name); |
188 | return FALSE; |
189 | } |
190 | |
191 | |
192 | |
193 | /* Unload the module. We use a call_gclose boolean to keep the g_module_close call |
194 | * after the call to the module's destroy function. */ |
195 | |
196 | int lttv_module_unload(lttv_module_info *moduleInfo, loadtype load) { |
197 | |
198 | lttv_module_unload_destroy destroy_Function; |
199 | |
200 | char *moduleName ; |
201 | |
202 | gboolean call_gclose = FALSE; |
203 | |
204 | if(moduleInfo == NULL) return FALSE; |
205 | |
206 | /* Closing the module decrements the usage counter if previously higher than |
207 | * 1. If 1, it unloads the module. |
208 | */ |
209 | |
210 | /* Add the module in the standalone array if the module is |
211 | * standalone and not in the array. Otherwise, set index to |
212 | * -1 (dependant only). |
213 | */ |
214 | if(load == STANDALONE) { |
215 | |
216 | if(moduleInfo->index_standalone == -1) { |
217 | |
218 | g_warning("Module %s is not loaded standalone.",moduleInfo->pathname); |
219 | } |
220 | else { |
221 | /* We do not remove the element of the array, it would change |
222 | * the index orders. We will have to check if index is -1 in |
223 | * unload all modules. |
224 | */ |
225 | moduleInfo->index_standalone = -1; |
226 | g_message("Unloading module %s, reference count passes from %u to %u", |
227 | moduleInfo->pathname,moduleInfo->ref_count, |
228 | moduleInfo->ref_count-1); |
229 | |
230 | moduleInfo->ref_count-- ; |
231 | call_gclose = TRUE ; |
232 | } |
233 | } |
234 | else { /* DEPENDANT */ |
235 | g_message("Unloading module %s, reference count passes from %u to %u", |
236 | moduleInfo->pathname, |
237 | moduleInfo->ref_count,moduleInfo->ref_count-1); |
238 | |
239 | moduleInfo->ref_count-- ; |
240 | call_gclose = TRUE ; |
241 | } |
242 | |
243 | /* The module is really closing if ref_count is 0 */ |
244 | if(!moduleInfo->ref_count) { |
245 | g_message("Unloading module %s : closing module.",moduleInfo->pathname); |
246 | |
247 | /* Call the destroy function of the module */ |
248 | if(!g_module_symbol(moduleInfo->module, "destroy", (gpointer) &destroy_Function)) { |
249 | g_critical("module %s (%s) does not have destroy function", |
250 | moduleInfo->pathname,moduleInfo->name); |
251 | } |
252 | else { |
253 | destroy_Function(); |
254 | } |
255 | |
256 | /* If the module will effectively be closed, remove the moduleInfo from |
257 | * the hash table and free the module name. |
258 | */ |
259 | g_free(moduleInfo->name) ; |
260 | |
261 | g_hash_table_remove(modules, moduleInfo->pathname); |
262 | } |
263 | |
264 | if(call_gclose) g_module_close(moduleInfo->module) ; |
265 | |
266 | return TRUE ; |
267 | } |
268 | |
269 | #define MODULE_I ((lttv_module_info *)modulesStandalone->pdata[i]) |
fcdf0ec2 |
270 | //FIXME use g_ptr_array_index instead |
eccb5352 |
271 | /* unload all the modules in the hash table, calling module_destroy for |
272 | * each of them. |
273 | * |
274 | * We first take all the moduleInfo in the hash table, put it in an |
275 | * array. We use qsort on the array to have the use count of 1 first. |
276 | */ |
277 | void lttv_module_unload_all() { |
278 | |
279 | int i = 0; |
280 | |
281 | /* call the unload for each module. |
282 | */ |
283 | for(i = 0; i < modulesStandalone->len; i++) { |
284 | |
285 | if(MODULE_I->index_standalone != -1) { |
286 | lttv_module_unload(MODULE_I,STANDALONE) ; |
287 | } |
288 | } |
289 | |
290 | } |