1 /*
2 * Copyright 2002-2008, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Copyright 2001, Thomas Kurschel. All rights reserved.
6 * Distributed under the terms of the NewOS License.
7 */
8
9 /** Manages kernel add-ons and their exported modules. */
10
11 #include "module.h"
12
13 #include <stdlib.h>
14
15 #include "fssh_errors.h"
16 #include "fssh_kernel_export.h"
17 #include "fssh_lock.h"
18 #include "fssh_module.h"
19 #include "fssh_string.h"
20 #include "hash.h"
21
22
23 //#define TRACE_MODULE
24 #ifdef TRACE_MODULE
25 # define TRACE(x) fssh_dprintf x
26 #else
27 # define TRACE(x) ;
28 #endif
29 #define FATAL(x) fssh_dprintf x
30
31
32 namespace FSShell {
33
34
35 #define MODULE_HASH_SIZE 16
36
37 enum module_state {
38 MODULE_QUERIED = 0,
39 MODULE_LOADED,
40 MODULE_INIT,
41 MODULE_READY,
42 MODULE_UNINIT,
43 MODULE_ERROR
44 };
45
46
47 /* Each known module will have this structure which is put in the
48 * gModulesHash, and looked up by name.
49 */
50
51 struct module {
52 struct module *next;
53 char *name;
54 char *file;
55 int32_t ref_count;
56 fssh_module_info *info; /* will only be valid if ref_count > 0 */
57 int32_t offset; /* this is the offset in the headers */
58 module_state state; /* state of module */
59 uint32_t flags;
60 };
61
62 #define FSSH_B_BUILT_IN_MODULE 2
63
64
65 /* locking scheme: there is a global lock only; having several locks
66 * makes trouble if dependent modules get loaded concurrently ->
67 * they have to wait for each other, i.e. we need one lock per module;
68 * also we must detect circular references during init and not dead-lock
69 */
70 static fssh_recursive_lock sModulesLock;
71
72 /* we store the loaded modules by directory path, and all known modules by module name
73 * in a hash table for quick access
74 */
75 static hash_table *sModulesHash;
76
77
78 /** calculates hash for a module using its name */
79
80 static uint32_t
module_hash(void * _module,const void * _key,uint32_t range)81 module_hash(void *_module, const void *_key, uint32_t range)
82 {
83 module *module = (struct module *)_module;
84 const char *name = (const char *)_key;
85
86 if (module != NULL)
87 return hash_hash_string(module->name) % range;
88
89 if (name != NULL)
90 return hash_hash_string(name) % range;
91
92 return 0;
93 }
94
95
96 /** compares a module to a given name */
97
98 static int
module_compare(void * _module,const void * _key)99 module_compare(void *_module, const void *_key)
100 {
101 module *module = (struct module *)_module;
102 const char *name = (const char *)_key;
103 if (name == NULL)
104 return -1;
105
106 return fssh_strcmp(module->name, name);
107 }
108
109
110 static inline void
inc_module_ref_count(struct module * module)111 inc_module_ref_count(struct module *module)
112 {
113 module->ref_count++;
114 }
115
116
117 static inline void
dec_module_ref_count(struct module * module)118 dec_module_ref_count(struct module *module)
119 {
120 module->ref_count--;
121 }
122
123
124 /** Extract the information from the module_info structure pointed at
125 * by "info" and create the entries required for access to it's details.
126 */
127
128 static fssh_status_t
create_module(fssh_module_info * info,const char * file,int offset,module ** _module)129 create_module(fssh_module_info *info, const char *file, int offset, module **_module)
130 {
131 module *module;
132
133 TRACE(("create_module(info = %p, file = \"%s\", offset = %d, _module = %p)\n",
134 info, file, offset, _module));
135
136 if (!info->name)
137 return FSSH_B_BAD_VALUE;
138
139 module = (struct module *)hash_lookup(sModulesHash, info->name);
140 if (module) {
141 FATAL(("Duplicate module name (%s) detected... ignoring new one\n", info->name));
142 return FSSH_B_FILE_EXISTS;
143 }
144
145 if ((module = (struct module *)malloc(sizeof(struct module))) == NULL)
146 return FSSH_B_NO_MEMORY;
147
148 TRACE(("create_module: name = \"%s\", file = \"%s\"\n", info->name, file));
149
150 module->name = fssh_strdup(info->name);
151 if (module->name == NULL) {
152 free(module);
153 return FSSH_B_NO_MEMORY;
154 }
155
156 module->file = fssh_strdup(file);
157 if (module->file == NULL) {
158 free(module->name);
159 free(module);
160 return FSSH_B_NO_MEMORY;
161 }
162
163 module->state = MODULE_QUERIED;
164 module->info = info;
165 module->offset = offset;
166 // record where the module_info can be found in the module_info array
167 module->ref_count = 0;
168 module->flags = info->flags;
169
170 fssh_recursive_lock_lock(&sModulesLock);
171 hash_insert(sModulesHash, module);
172 fssh_recursive_lock_unlock(&sModulesLock);
173
174 if (_module)
175 *_module = module;
176
177 return FSSH_B_OK;
178 }
179
180
181 /** Initializes a loaded module depending on its state */
182
183 static inline fssh_status_t
init_module(module * module)184 init_module(module *module)
185 {
186 switch (module->state) {
187 case MODULE_QUERIED:
188 case MODULE_LOADED:
189 {
190 fssh_status_t status;
191 module->state = MODULE_INIT;
192
193 // init module
194
195 TRACE(("initializing module %s (at %p)... \n", module->name, module->info->std_ops));
196 status = module->info->std_ops(FSSH_B_MODULE_INIT);
197 TRACE(("...done (%s)\n", strerror(status)));
198
199 if (status >= FSSH_B_OK)
200 module->state = MODULE_READY;
201 else {
202 module->state = MODULE_LOADED;
203 }
204
205 return status;
206 }
207
208 case MODULE_READY:
209 return FSSH_B_OK;
210
211 case MODULE_INIT:
212 FATAL(("circular reference to %s\n", module->name));
213 return FSSH_B_ERROR;
214
215 case MODULE_UNINIT:
216 FATAL(("tried to load module %s which is currently unloading\n", module->name));
217 return FSSH_B_ERROR;
218
219 case MODULE_ERROR:
220 FATAL(("cannot load module %s because its earlier unloading failed\n", module->name));
221 return FSSH_B_ERROR;
222
223 default:
224 return FSSH_B_ERROR;
225 }
226 // never trespasses here
227 }
228
229
230 /** Uninitializes a module depeding on its state */
231
232 static inline int
uninit_module(module * module)233 uninit_module(module *module)
234 {
235 TRACE(("uninit_module(%s)\n", module->name));
236
237 switch (module->state) {
238 case MODULE_QUERIED:
239 case MODULE_LOADED:
240 return FSSH_B_NO_ERROR;
241
242 case MODULE_INIT:
243 fssh_panic("Trying to unload module %s which is initializing\n", module->name);
244 return FSSH_B_ERROR;
245
246 case MODULE_UNINIT:
247 fssh_panic("Trying to unload module %s which is un-initializing\n", module->name);
248 return FSSH_B_ERROR;
249
250 case MODULE_READY:
251 {
252 fssh_status_t status;
253
254 module->state = MODULE_UNINIT;
255
256 TRACE(("uninitializing module %s...\n", module->name));
257 status = module->info->std_ops(FSSH_B_MODULE_UNINIT);
258 TRACE(("...done (%s)\n", strerror(status)));
259
260 if (status == FSSH_B_NO_ERROR) {
261 module->state = MODULE_LOADED;
262 return 0;
263 }
264
265 FATAL(("Error unloading module %s (%s)\n", module->name, fssh_strerror(status)));
266
267 module->state = MODULE_ERROR;
268 module->flags |= FSSH_B_KEEP_LOADED;
269
270 return status;
271 }
272 default:
273 return FSSH_B_ERROR;
274 }
275 // never trespasses here
276 }
277
278
279 void
register_builtin_module(struct fssh_module_info * info)280 register_builtin_module(struct fssh_module_info *info)
281 {
282 info->flags |= FSSH_B_BUILT_IN_MODULE;
283 // this is an internal flag, it doesn't have to be set by modules itself
284
285 if (create_module(info, "", -1, NULL) != FSSH_B_OK)
286 fssh_dprintf("creation of built-in module \"%s\" failed!\n", info->name);
287 }
288
289
290 static int
dump_modules(int argc,char ** argv)291 dump_modules(int argc, char **argv)
292 {
293 hash_iterator iterator;
294 struct module *module;
295
296 hash_rewind(sModulesHash, &iterator);
297 fssh_dprintf("-- known modules:\n");
298
299 while ((module = (struct module *)hash_next(sModulesHash, &iterator)) != NULL) {
300 fssh_dprintf("%p: \"%s\", \"%s\" (%d), refcount = %d, state = %d\n",
301 module, module->name, module->file, (int)module->offset, (int)module->ref_count,
302 module->state);
303 }
304
305 return 0;
306 }
307
308
309 // #pragma mark -
310 // Exported Kernel API (private part)
311
312
313 /** Setup the module structures and data for use - must be called
314 * before any other module call.
315 */
316
317 fssh_status_t
module_init(kernel_args * args)318 module_init(kernel_args *args)
319 {
320 fssh_recursive_lock_init(&sModulesLock, "modules rlock");
321
322 sModulesHash = hash_init(MODULE_HASH_SIZE, 0, module_compare, module_hash);
323 if (sModulesHash == NULL)
324 return FSSH_B_NO_MEMORY;
325
326 fssh_add_debugger_command("modules", &dump_modules, "list all known & loaded modules");
327
328 return FSSH_B_OK;
329 }
330
331
332 } // namespace FSShell
333
334
335 // #pragma mark -
336 // Exported Kernel API (public part)
337
338
339 using namespace FSShell;
340
341
342 fssh_status_t
fssh_get_module(const char * path,fssh_module_info ** _info)343 fssh_get_module(const char *path, fssh_module_info **_info)
344 {
345 module *module;
346 fssh_status_t status;
347
348 TRACE(("get_module(%s)\n", path));
349
350 if (path == NULL)
351 return FSSH_B_BAD_VALUE;
352
353 fssh_recursive_lock_lock(&sModulesLock);
354
355 module = (struct module *)hash_lookup(sModulesHash, path);
356 if (module == NULL)
357 goto err;
358
359 // The state will be adjusted by the call to init_module
360 // if we have just loaded the file
361 if (module->ref_count == 0)
362 status = init_module(module);
363 else
364 status = FSSH_B_OK;
365
366 if (status == FSSH_B_OK) {
367 inc_module_ref_count(module);
368 *_info = module->info;
369 }
370
371 fssh_recursive_lock_unlock(&sModulesLock);
372 return status;
373
374 err:
375 fssh_recursive_lock_unlock(&sModulesLock);
376 return FSSH_B_ENTRY_NOT_FOUND;
377 }
378
379
380 fssh_status_t
fssh_put_module(const char * path)381 fssh_put_module(const char *path)
382 {
383 module *module;
384
385 TRACE(("put_module(path = %s)\n", path));
386
387 fssh_recursive_lock_lock(&sModulesLock);
388
389 module = (struct module *)hash_lookup(sModulesHash, path);
390 if (module == NULL) {
391 FATAL(("module: We don't seem to have a reference to module %s\n", path));
392 fssh_recursive_lock_unlock(&sModulesLock);
393 return FSSH_B_BAD_VALUE;
394 }
395
396 if ((module->flags & FSSH_B_KEEP_LOADED) == 0) {
397 dec_module_ref_count(module);
398
399 if (module->ref_count == 0)
400 uninit_module(module);
401 }
402
403 fssh_recursive_lock_unlock(&sModulesLock);
404 return FSSH_B_OK;
405 }
406