* Copyright 2002-2008, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Copyright 2001, Thomas Kurschel. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include "module.h"
#include <stdlib.h>
#include "fssh_errors.h"
#include "fssh_kernel_export.h"
#include "fssh_lock.h"
#include "fssh_module.h"
#include "fssh_string.h"
#include "hash.h"
#ifdef TRACE_MODULE
# define TRACE(x) fssh_dprintf x
#else
# define TRACE(x) ;
#endif
#define FATAL(x) fssh_dprintf x
namespace FSShell {
#define MODULE_HASH_SIZE 16
enum module_state {
MODULE_QUERIED = 0,
MODULE_LOADED,
MODULE_INIT,
MODULE_READY,
MODULE_UNINIT,
MODULE_ERROR
};
* gModulesHash, and looked up by name.
*/
struct module {
struct module *next;
char *name;
char *file;
int32_t ref_count;
fssh_module_info *info;
int32_t offset;
module_state state;
uint32_t flags;
};
#define FSSH_B_BUILT_IN_MODULE 2
* makes trouble if dependent modules get loaded concurrently ->
* they have to wait for each other, i.e. we need one lock per module;
* also we must detect circular references during init and not dead-lock
*/
static fssh_recursive_lock sModulesLock;
* in a hash table for quick access
*/
static hash_table *sModulesHash;
static uint32_t
module_hash(void *_module, const void *_key, uint32_t range)
{
module *module = (struct module *)_module;
const char *name = (const char *)_key;
if (module != NULL)
return hash_hash_string(module->name) % range;
if (name != NULL)
return hash_hash_string(name) % range;
return 0;
}
static int
module_compare(void *_module, const void *_key)
{
module *module = (struct module *)_module;
const char *name = (const char *)_key;
if (name == NULL)
return -1;
return fssh_strcmp(module->name, name);
}
static inline void
inc_module_ref_count(struct module *module)
{
module->ref_count++;
}
static inline void
dec_module_ref_count(struct module *module)
{
module->ref_count--;
}
* by "info" and create the entries required for access to it's details.
*/
static fssh_status_t
create_module(fssh_module_info *info, const char *file, int offset, module **_module)
{
module *module;
TRACE(("create_module(info = %p, file = \"%s\", offset = %d, _module = %p)\n",
info, file, offset, _module));
if (!info->name)
return FSSH_B_BAD_VALUE;
module = (struct module *)hash_lookup(sModulesHash, info->name);
if (module) {
FATAL(("Duplicate module name (%s) detected... ignoring new one\n", info->name));
return FSSH_B_FILE_EXISTS;
}
if ((module = (struct module *)malloc(sizeof(struct module))) == NULL)
return FSSH_B_NO_MEMORY;
TRACE(("create_module: name = \"%s\", file = \"%s\"\n", info->name, file));
module->name = fssh_strdup(info->name);
if (module->name == NULL) {
free(module);
return FSSH_B_NO_MEMORY;
}
module->file = fssh_strdup(file);
if (module->file == NULL) {
free(module->name);
free(module);
return FSSH_B_NO_MEMORY;
}
module->state = MODULE_QUERIED;
module->info = info;
module->offset = offset;
module->ref_count = 0;
module->flags = info->flags;
fssh_recursive_lock_lock(&sModulesLock);
hash_insert(sModulesHash, module);
fssh_recursive_lock_unlock(&sModulesLock);
if (_module)
*_module = module;
return FSSH_B_OK;
}
static inline fssh_status_t
init_module(module *module)
{
switch (module->state) {
case MODULE_QUERIED:
case MODULE_LOADED:
{
fssh_status_t status;
module->state = MODULE_INIT;
TRACE(("initializing module %s (at %p)... \n", module->name, module->info->std_ops));
status = module->info->std_ops(FSSH_B_MODULE_INIT);
TRACE(("...done (%s)\n", strerror(status)));
if (status >= FSSH_B_OK)
module->state = MODULE_READY;
else {
module->state = MODULE_LOADED;
}
return status;
}
case MODULE_READY:
return FSSH_B_OK;
case MODULE_INIT:
FATAL(("circular reference to %s\n", module->name));
return FSSH_B_ERROR;
case MODULE_UNINIT:
FATAL(("tried to load module %s which is currently unloading\n", module->name));
return FSSH_B_ERROR;
case MODULE_ERROR:
FATAL(("cannot load module %s because its earlier unloading failed\n", module->name));
return FSSH_B_ERROR;
default:
return FSSH_B_ERROR;
}
}
static inline int
uninit_module(module *module)
{
TRACE(("uninit_module(%s)\n", module->name));
switch (module->state) {
case MODULE_QUERIED:
case MODULE_LOADED:
return FSSH_B_NO_ERROR;
case MODULE_INIT:
fssh_panic("Trying to unload module %s which is initializing\n", module->name);
return FSSH_B_ERROR;
case MODULE_UNINIT:
fssh_panic("Trying to unload module %s which is un-initializing\n", module->name);
return FSSH_B_ERROR;
case MODULE_READY:
{
fssh_status_t status;
module->state = MODULE_UNINIT;
TRACE(("uninitializing module %s...\n", module->name));
status = module->info->std_ops(FSSH_B_MODULE_UNINIT);
TRACE(("...done (%s)\n", strerror(status)));
if (status == FSSH_B_NO_ERROR) {
module->state = MODULE_LOADED;
return 0;
}
FATAL(("Error unloading module %s (%s)\n", module->name, fssh_strerror(status)));
module->state = MODULE_ERROR;
module->flags |= FSSH_B_KEEP_LOADED;
return status;
}
default:
return FSSH_B_ERROR;
}
}
void
register_builtin_module(struct fssh_module_info *info)
{
info->flags |= FSSH_B_BUILT_IN_MODULE;
if (create_module(info, "", -1, NULL) != FSSH_B_OK)
fssh_dprintf("creation of built-in module \"%s\" failed!\n", info->name);
}
static int
dump_modules(int argc, char **argv)
{
hash_iterator iterator;
struct module *module;
hash_rewind(sModulesHash, &iterator);
fssh_dprintf("-- known modules:\n");
while ((module = (struct module *)hash_next(sModulesHash, &iterator)) != NULL) {
fssh_dprintf("%p: \"%s\", \"%s\" (%d), refcount = %d, state = %d\n",
module, module->name, module->file, (int)module->offset, (int)module->ref_count,
module->state);
}
return 0;
}
* before any other module call.
*/
fssh_status_t
module_init(kernel_args *args)
{
fssh_recursive_lock_init(&sModulesLock, "modules rlock");
sModulesHash = hash_init(MODULE_HASH_SIZE, 0, module_compare, module_hash);
if (sModulesHash == NULL)
return FSSH_B_NO_MEMORY;
fssh_add_debugger_command("modules", &dump_modules, "list all known & loaded modules");
return FSSH_B_OK;
}
}
using namespace FSShell;
fssh_status_t
fssh_get_module(const char *path, fssh_module_info **_info)
{
module *module;
fssh_status_t status;
TRACE(("get_module(%s)\n", path));
if (path == NULL)
return FSSH_B_BAD_VALUE;
fssh_recursive_lock_lock(&sModulesLock);
module = (struct module *)hash_lookup(sModulesHash, path);
if (module == NULL)
goto err;
if (module->ref_count == 0)
status = init_module(module);
else
status = FSSH_B_OK;
if (status == FSSH_B_OK) {
inc_module_ref_count(module);
*_info = module->info;
}
fssh_recursive_lock_unlock(&sModulesLock);
return status;
err:
fssh_recursive_lock_unlock(&sModulesLock);
return FSSH_B_ENTRY_NOT_FOUND;
}
fssh_status_t
fssh_put_module(const char *path)
{
module *module;
TRACE(("put_module(path = %s)\n", path));
fssh_recursive_lock_lock(&sModulesLock);
module = (struct module *)hash_lookup(sModulesHash, path);
if (module == NULL) {
FATAL(("module: We don't seem to have a reference to module %s\n", path));
fssh_recursive_lock_unlock(&sModulesLock);
return FSSH_B_BAD_VALUE;
}
if ((module->flags & FSSH_B_KEEP_LOADED) == 0) {
dec_module_ref_count(module);
if (module->ref_count == 0)
uninit_module(module);
}
fssh_recursive_lock_unlock(&sModulesLock);
return FSSH_B_OK;
}