xref: /haiku/src/tools/fs_shell/module.cpp (revision 589f1a9133081a871738156ebc2c95e757581aac)
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